<?xml version="1.0" encoding="UTF-8"?>
<rdf:RDF xmlns="http://purl.org/rss/1.0/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xml:lang="ja-JP">
	<channel rdf:about="http://ogawa.s18.xrea.com/tdiary/index.rdf">
	<title>メモの日々</title>
	<link>http://ogawa.s18.xrea.com/tdiary/</link>
	<description>日記です</description>
	<dc:creator>OGAWA KenIchi</dc:creator>
	<dc:rights>Copyright 2026 OGAWA KenIchi, copyright of comments by respective authors</dc:rights>
	<items><rdf:Seq>
<rdf:li rdf:resource="http://ogawa.s18.xrea.com/tdiary/20260423p01.html#p01"/>
<rdf:li rdf:resource="http://ogawa.s18.xrea.com/tdiary/20260408p01.html#p01"/>
<rdf:li rdf:resource="http://ogawa.s18.xrea.com/tdiary/20251203p01.html#p01"/>
<rdf:li rdf:resource="http://ogawa.s18.xrea.com/tdiary/20251127p01.html#p01"/>
<rdf:li rdf:resource="http://ogawa.s18.xrea.com/tdiary/20251122p01.html#p01"/>
<rdf:li rdf:resource="http://ogawa.s18.xrea.com/tdiary/20250528p01.html#p01"/>
<rdf:li rdf:resource="http://ogawa.s18.xrea.com/tdiary/20250328p01.html#p01"/>
<rdf:li rdf:resource="http://ogawa.s18.xrea.com/tdiary/20250220p01.html#p01"/>
<rdf:li rdf:resource="http://ogawa.s18.xrea.com/tdiary/20250116p01.html#p01"/>
<rdf:li rdf:resource="http://ogawa.s18.xrea.com/tdiary/20241126p01.html#p01"/>
<rdf:li rdf:resource="http://ogawa.s18.xrea.com/tdiary/20241008p01.html#p01"/>
<rdf:li rdf:resource="http://ogawa.s18.xrea.com/tdiary/20241001p01.html#p01"/>
<rdf:li rdf:resource="http://ogawa.s18.xrea.com/tdiary/20240927p01.html#p01"/>
<rdf:li rdf:resource="http://ogawa.s18.xrea.com/tdiary/20240926p01.html#p01"/>
<rdf:li rdf:resource="http://ogawa.s18.xrea.com/tdiary/20240924p01.html#p01"/>
</rdf:Seq></items>
</channel>
<item rdf:about="http://ogawa.s18.xrea.com/tdiary/20260423p01.html#p01">
<link>http://ogawa.s18.xrea.com/tdiary/20260423p01.html#p01</link>
<dc:date>2026-04-27T14:05:18+09:00</dc:date>
<title>Pixel 10a を買った</title>
<dc:creator>OGAWA KenIchi</dc:creator>
<dc:subject>hard</dc:subject>
<dc:subject>android</dc:subject>
<description>2年前に買ったPixel 8aからPixel 10aへ乗り換え。 79900円で7000円くらい値上がり。Spigenのケースは2580円。 移行時に行った設定をメモ。  データの手動移行 LINE QRコードを使った引き継ぎ。事前にデータのサーバーへのバックアップが必要。  PayPay ログイン後、QRコードを使った認証を行う。  QUICPay Googleウォレットで新しいカードを追加する操作をすると旧端末に登録していたデータを選ぶことができ、移行操作した  みずほダイレクト 【みずほダイレクトアプリ】Android→Androidに機種変更する場合の引き継ぎ方法を知りたい。にあるようにする  マイナポータル スマホ用電子証明書の利用申請。マイナンバーカードが必要。  Authenticator バックアップからの復元を行う  AntennaPod 設定メニューからファイルのエクスポートとインポート  駒サプリ データの復元をするためにインストールし直しが必要だった。また、旧端末でバックアップ操作が必要。  トモズ 情報入力  歯医者 情報入力（旧端末上で表示できる）   ..</description>
<content:encoded><![CDATA[<h3>Pixel 10a を買った</h3><p><a href="http://ogawa.s18.xrea.com/tdiary/20240609p01.html#p01">2年前</a>に買ったPixel 8aから<a href="https://store.google.com/jp/product/pixel_10a">Pixel 10a</a>へ乗り換え。</p>
<p>79900円で7000円くらい値上がり。<a href="https://www.amazon.co.jp/dp/B0FZPY2FT6">Spigenのケース</a>は2580円。</p>
<p>移行時に行った設定をメモ。</p>
<ul>
<li>データの手動移行<ul>
<li>LINE<ul>
<li>QRコードを使った引き継ぎ。事前にデータのサーバーへのバックアップが必要。</li>
</ul></li>
<li>PayPay<ul>
<li>ログイン後、QRコードを使った認証を行う。</li>
</ul></li>
<li>QUICPay<ul>
<li>Googleウォレットで新しいカードを追加する操作をすると旧端末に登録していたデータを選ぶことができ、移行操作した</li>
</ul></li>
<li>みずほダイレクト<ul>
<li><a href="https://www.faq.mizuhobank.co.jp/faq/show/12586">【みずほダイレクトアプリ】Android→Androidに機種変更する場合の引き継ぎ方法を知りたい。</a>にあるようにする</li>
</ul></li>
<li>マイナポータル<ul>
<li>スマホ用電子証明書の利用申請。マイナンバーカードが必要。</li>
</ul></li>
<li>Authenticator<ul>
<li>バックアップからの復元を行う</li>
</ul></li>
<li>AntennaPod<ul>
<li>設定メニューからファイルのエクスポートとインポート</li>
</ul></li>
<li>駒サプリ<ul>
<li>データの復元をするためにインストールし直しが必要だった。また、旧端末でバックアップ操作が必要。</li>
</ul></li>
<li>トモズ<ul>
<li>情報入力</li>
</ul></li>
<li>歯医者<ul>
<li>情報入力（旧端末上で表示できる）</li>
</ul></li>
</ul></li>
<li>ログインし直し<ul>
<li>X</li>
<li>モバイルSuica<ul>
<li>サーバーを介したデータの移動はしていない。最初のAndroidのデータ移行処理によってチャージ金額等が自動的に新端末へ移動されていた。Googleウォレットの機能？</li>
</ul></li>
<li>Authy</li>
<li>はてなブックマーク<ul>
<li>ログインしようとしたらパスキー登録が行われた</li>
</ul></li>
<li>ビックカメラ</li>
<li>マクドナルド</li>
<li>出前館</li>
<li>セゾン</li>
<li>どうぶつしょうぎウォーズ</li>
<li>囲碁クエスト</li>
<li>Yahooファイナンス</li>
<li>IIJ</li>
<li>asahi</li>
<li>ノジマ<ul>
<li>アプリに「機種変更手続き」というメニューがあったが使わなかった</li>
</ul></li>
<li>Zepp Life (Google)</li>
<li>Tile</li>
<li>東京アプリ</li>
<li>入居者アプリ</li>
<li>テトル</li>
</ul></li>
</ul>
<p><a href="http://ogawa.s18.xrea.com/tdiary/20260423.html#c">ツッコミを入れる</a></p>]]></content:encoded>
</item>
<item rdf:about="http://ogawa.s18.xrea.com/tdiary/20260408p01.html#p01">
<link>http://ogawa.s18.xrea.com/tdiary/20260408p01.html#p01</link>
<dc:date>2026-04-08T16:34:49+09:00</dc:date>
<title>tmuxでペインの内容をファイルに出力する (2)</title>
<dc:creator>OGAWA KenIchi</dc:creator>
<dc:subject>howto</dc:subject>
<description>以前に、tmuxコマンドのサブコマンドにcapture-paneを指定してペインの内容をファイルに出力する方法をメモしていた。 tmuxコマンドを使うのではなく、tmuxのコマンドプロンプト（「プレフィックスキーの後に:」で入れるモード）からファイル出力する方法をメモ。 capture-pane -S - ; save-buffer 出力ファイル名  capture-paneの -S オプションに - を指定するとペインの内容全部がバッファーへ保存される save-bufferでバッファーの内容をファイルへ出力できる tmuxのコマンドは「;」で連結できる</description>
<content:encoded><![CDATA[<h3>tmuxでペインの内容をファイルに出力する (2)</h3><p><a href="http://ogawa.s18.xrea.com/tdiary/20160412p02.html#p02">以前</a>に、tmuxコマンドのサブコマンドにcapture-paneを指定してペインの内容をファイルに出力する方法をメモしていた。</p>
<p>tmuxコマンドを使うのではなく、tmuxのコマンドプロンプト（「プレフィックスキーの後に:」で入れるモード）からファイル出力する方法をメモ。</p>
<pre>capture-pane -S - ; save-buffer 出力ファイル名</pre>
<ul>
<li><a href="https://man.openbsd.org/OpenBSD-current/man1/tmux.1#capture-pane">capture-pane</a>の -S オプションに - を指定するとペインの内容全部がバッファーへ保存される</li>
<li><a href="https://man.openbsd.org/OpenBSD-current/man1/tmux.1#save-buffer">save-buffer</a>でバッファーの内容をファイルへ出力できる</li>
<li>tmuxのコマンドは<a href="https://man.openbsd.org/OpenBSD-current/man1/tmux.1#PARSING_SYNTAX">「;」で連結できる</a></li>
</ul>
<p><a href="http://ogawa.s18.xrea.com/tdiary/20260408.html#c">ツッコミを入れる</a></p>]]></content:encoded>
</item>
<item rdf:about="http://ogawa.s18.xrea.com/tdiary/20251203p01.html#p01">
<link>http://ogawa.s18.xrea.com/tdiary/20251203p01.html#p01</link>
<dc:date>2025-12-03T17:24:27+09:00</dc:date>
<title>C++で宣言に書く指定子の順序</title>
<dc:creator>OGAWA KenIchi</dc:creator>
<dc:subject>c++</dc:subject>
<description>C++の宣言に書く指定子の順序に言及した文書が https://github.com/cplusplus/draft のWikiにあったのでメモ。  Specification Style Guidelines (github.com/cplusplus)  In a decl-specifier-seq, the decl-specifiers should be written in the following order:  friend / typedef / storage-class-specifier / virtual inline constexpr explicit-specifier const volatile unsigned / signed short / long other type-specifiers</description>
<content:encoded><![CDATA[<h3>C++で宣言に書く指定子の順序</h3><p>C++の宣言に書く指定子の順序に言及した文書が <a href="https://github.com/cplusplus/draft">https://github.com/cplusplus/draft</a> のWikiにあったのでメモ。</p>
<ul>
<li><a href="https://github.com/cplusplus/draft/wiki/Specification-Style-Guidelines#user-content-formatting-declarations-and-definitions">Specification Style Guidelines</a> (github.com/cplusplus)</li>
</ul>
<blockquote><p>In a decl-specifier-seq, the decl-specifiers should be written in the following order:</p>
<ol>
<li>friend / typedef / storage-class-specifier / virtual</li>
<li>inline</li>
<li>constexpr</li>
<li>explicit-specifier</li>
<li>const</li>
<li>volatile</li>
<li>unsigned / signed</li>
<li>short / long</li>
<li>other type-specifiers</li>
</ol>
</blockquote>
<p><a href="http://ogawa.s18.xrea.com/tdiary/20251203.html#c">ツッコミを入れる</a></p>]]></content:encoded>
</item>
<item rdf:about="http://ogawa.s18.xrea.com/tdiary/20251127p01.html#p01">
<link>http://ogawa.s18.xrea.com/tdiary/20251127p01.html#p01</link>
<dc:date>2025-11-27T01:08:37+09:00</dc:date>
<title>datetimeオブジェクトへ明示的にタイムゾーンを付与する</title>
<dc:creator>OGAWA KenIchi</dc:creator>
<dc:subject>python</dc:subject>
<description>以前にシステムローカルのタイムゾーンを付与する方法をメモした。 明示的にタイムゾーンを指定したい場合はどうしたらいいか。datetime.astimezone()だとうまくいかない。  import datetime  dt = datetime.datetime(2023, 1, 1)  dt.isoformat() &#39;2023-01-01T00:00:00&#39;  dt.astimezone(datetime.timezone.utc).isoformat() &#39;2022-12-31T15:00:00+00:00&#39; 日付が変わってしまった。datetime.replace()を使うとうまくいく。  dt.replace(tzinfo=datetime.timezone.utc).isoformat() &#39;2023-01-01T00:00:00+00:00&#39;</description>
<content:encoded><![CDATA[<h3>datetimeオブジェクトへ明示的にタイムゾーンを付与する</h3><p><a href="http://ogawa.s18.xrea.com/tdiary/20230101p01.html#p01">以前</a>にシステムローカルのタイムゾーンを付与する方法をメモした。</p>
<p>明示的にタイムゾーンを指定したい場合はどうしたらいいか。<a href="https://docs.python.org/ja/3.15/library/datetime.html#datetime.datetime.astimezone">datetime.astimezone()</a>だとうまくいかない。</p>
<pre>&gt;&gt;&gt; import datetime
&gt;&gt;&gt; dt = datetime.datetime(2023, 1, 1)
&gt;&gt;&gt; dt.isoformat()
'2023-01-01T00:00:00'
&gt;&gt;&gt; dt.astimezone(datetime.timezone.utc).isoformat()
'2022-12-31T15:00:00+00:00'</pre>
<p>日付が変わってしまった。<a href="https://docs.python.org/ja/3.15/library/datetime.html#datetime.datetime.replace">datetime.replace()</a>を使うとうまくいく。</p>
<pre>&gt;&gt;&gt; dt.replace(tzinfo=datetime.timezone.utc).isoformat()
'2023-01-01T00:00:00+00:00'</pre>
<p><a href="http://ogawa.s18.xrea.com/tdiary/20251127.html#c">ツッコミを入れる</a></p>]]></content:encoded>
</item>
<item rdf:about="http://ogawa.s18.xrea.com/tdiary/20251122p01.html#p01">
<link>http://ogawa.s18.xrea.com/tdiary/20251122p01.html#p01</link>
<dc:date>2025-11-22T02:07:40+09:00</dc:date>
<title>PostgreSQLでデータベースをコピーする</title>
<dc:creator>OGAWA KenIchi</dc:creator>
<dc:subject>db</dc:subject>
<description>久しぶりにPostgreSQLを使っている。バージョンは18.0。 CREATE TABLE文を使って手軽にデータベースをコピーできることを知ったのでメモ。次のようにすれば、src_dbのコピーとしてdst_dbを作れる。 create database dst_db template src_db; ただし、別のユーザーが接続中だとエラーになってコピーできないので注意。 マニュアルには次のような説明があった。  22.3. テンプレートデータベース  また、実際のところCREATE DATABASEのテンプレートとして名前を指定することで、クラスタ内の任意のデータベースをコピーできます。 しかし、この機能は、（まだ）汎用目的の「COPY DATABASE」能力を意図したものではないことを理解しておいてください。 コピー操作の間、他のセッションから元のデータベースに接続することができないという点は大きな制限です。 CREATE DATABASEは、その起動時に他の接続が存在する場合失敗します。 コピー操作中は元のデータベースへの新しい接続を許しません。</description>
<content:encoded><![CDATA[<h3>PostgreSQLでデータベースをコピーする</h3><p>久しぶりにPostgreSQLを使っている。バージョンは18.0。</p>
<p>CREATE TABLE文を使って手軽にデータベースをコピーできることを知ったのでメモ。次のようにすれば、src_dbのコピーとしてdst_dbを作れる。</p>
<pre>create database dst_db template src_db;</pre>
<p>ただし、別のユーザーが接続中だとエラーになってコピーできないので注意。</p>
<p>マニュアルには次のような説明があった。</p>
<ul>
<li><a href="https://www.postgresql.jp/document/17/html/manage-ag-templatedbs.html">22.3. テンプレートデータベース</a></li>
</ul>
<blockquote><p>また、実際のところCREATE DATABASEのテンプレートとして名前を指定することで、クラスタ内の任意のデータベースをコピーできます。 しかし、この機能は、（まだ）汎用目的の「COPY DATABASE」能力を意図したものではないことを理解しておいてください。 コピー操作の間、他のセッションから元のデータベースに接続することができないという点は大きな制限です。 CREATE DATABASEは、その起動時に他の接続が存在する場合失敗します。 コピー操作中は元のデータベースへの新しい接続を許しません。 </p>
</blockquote>
<p><a href="http://ogawa.s18.xrea.com/tdiary/20251122.html#c">ツッコミを入れる</a></p>]]></content:encoded>
</item>
<item rdf:about="http://ogawa.s18.xrea.com/tdiary/20250528p01.html#p01">
<link>http://ogawa.s18.xrea.com/tdiary/20250528p01.html#p01</link>
<dc:date>2025-05-28T11:21:00+09:00</dc:date>
<title>WSLが起動しなくなったが復旧した</title>
<dc:creator>OGAWA KenIchi</dc:creator>
<dc:subject>windows</dc:subject>
<description>気づいたらWindowsが再起動していた。原因不明（原因を知る方法あるのかな？）。 その後WSLを起動しようとしたら次のエラーが出力され起動できなくなっていた。 ディスク &#39;C:\Program Files\WSL\system.vhd&#39; を WSL2 にアタッチできませんでした: 指定されたファイルが見つかりません。 エラー コード: Wsl/Service/CreateInstance/CreateVm/MountDisk/HCS/ERROR_FILE_NOT_FOUND  不穏なメッセージだ…。C:\Program Files\WSL\system.vhd は存在していない。 検索すると、復旧はできるようだった。WSLの次のissueが参考になる。  WSL2. Can no longer find &#39;C:\Program Files\WSL\system.vhd&#39; (GitHub)  このissueのこのコメントにあるように、GitHubからWSLのmsiファイルをダウンロードして、その中に含まれるsystem.vhdを C:\Program Files\WSL\ へコピー..</description>
<content:encoded><![CDATA[<h3>WSLが起動しなくなったが復旧した</h3><p>気づいたらWindowsが再起動していた。原因不明（原因を知る方法あるのかな？）。</p>
<p>その後WSLを起動しようとしたら次のエラーが出力され起動できなくなっていた。</p>
<blockquote><p>ディスク 'C:\Program Files\WSL\system.vhd' を WSL2 にアタッチできませんでした: 指定されたファイルが見つかりません。</p>
<p>エラー コード: Wsl/Service/CreateInstance/CreateVm/MountDisk/HCS/ERROR_FILE_NOT_FOUND</p>
</blockquote>
<p>不穏なメッセージだ…。C:\Program Files\WSL\system.vhd は存在していない。</p>
<p>検索すると、復旧はできるようだった。WSLの次のissueが参考になる。</p>
<ul>
<li><a href="https://github.com/microsoft/WSL/issues/11069">WSL2. Can no longer find 'C:\Program Files\WSL\system.vhd'</a> (GitHub)</li>
</ul>
<p>このissueの<a href="https://github.com/microsoft/WSL/issues/11069#issuecomment-2719703464">このコメント</a>にあるように、GitHubからWSLのmsiファイルをダウンロードして、その中に含まれるsystem.vhdを C:\Program Files\WSL\ へコピーすればよい。</p>
<p>wsl --version コマンドで確認するとWSLのバージョンは2.5.7.0だったので、<a href="https://github.com/microsoft/WSL/releases">https://github.com/microsoft/WSL/releases</a>から wsl.2.5.7.0.x64.msi をダウンロードし、<a href="https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/msiexec">msiexecコマンド</a>を使い次のようにしてmsiファイルをどこかへ展開し</p>
<pre>start /wait msiexec /a wsl.2.5.7.0.x64.msi targetdir="C:\tmp\dokoka" /qn</pre>
<p>展開先の PFiles64\WSL\system.vhd を C:\Program Files\WSL\ へコピーした。今の所これで問題なく復旧したように思われる。</p>
<p>msiexecの使い方は次のページを参考にした。</p>
<ul>
<li><a href="https://atmarkit.itmedia.co.jp/ait/articles/0703/02/news130.html">Windowsで.MSIファイルを解凍して内部のファイルを取り出す（msiexec編）</a> (@IT)</li>
</ul>
<p><a href="http://ogawa.s18.xrea.com/tdiary/20250528.html#c">ツッコミを入れる</a></p>]]></content:encoded>
</item>
<item rdf:about="http://ogawa.s18.xrea.com/tdiary/20250328p01.html#p01">
<link>http://ogawa.s18.xrea.com/tdiary/20250328p01.html#p01</link>
<dc:date>2025-03-28T16:25:53+09:00</dc:date>
<title>test-unit-runner-junitxmlのリポジトリをtest-unit公式配下に移した</title>
<dc:creator>OGAWA KenIchi</dc:creator>
<dc:subject>ruby</dc:subject>
<description>6年前にリリースしたtest-unit-runner-junitxml というtest-unitのtest runnerのリポジトリを、https://github.com/test-unit 配下に置かせてもらうようになった。  https://github.com/test-unit/test-unit-runner-junitxml  次のissueがきっかけ。  Add support for outputting JUnit compatible XML log</description>
<content:encoded><![CDATA[<h3>test-unit-runner-junitxmlのリポジトリをtest-unit公式配下に移した</h3><p><a href="http://ogawa.s18.xrea.com/tdiary/20190319p01.html#p01">6年前</a>にリリースした<a href="https://rubygems.org/gems/test-unit-runner-junitxml">test-unit-runner-junitxml</a> という<a href="https://github.com/test-unit/test-unit/">test-unit</a>のtest runnerのリポジトリを、<a href="https://github.com/test-unit">https://github.com/test-unit</a> 配下に置かせてもらうようになった。</p>
<ul>
<li><a href="https://github.com/test-unit/test-unit-runner-junitxml">https://github.com/test-unit/test-unit-runner-junitxml</a></li>
</ul>
<p>次のissueがきっかけ。</p>
<ul>
<li><a href="https://github.com/test-unit/test-unit/issues/305">Add support for outputting JUnit compatible XML log</a></li>
</ul>
<p><a href="http://ogawa.s18.xrea.com/tdiary/20250328.html#c">ツッコミを入れる</a></p>]]></content:encoded>
</item>
<item rdf:about="http://ogawa.s18.xrea.com/tdiary/20250220p01.html#p01">
<link>http://ogawa.s18.xrea.com/tdiary/20250220p01.html#p01</link>
<dc:date>2025-02-20T16:10:25+09:00</dc:date>
<title>C++の正規表現メモ</title>
<dc:creator>OGAWA KenIchi</dc:creator>
<dc:subject>c++</dc:subject>
<description>regexの使い方をメモ。 正規表現オブジェクトとそれを使った文字列に対するマッチ処理について以下をメモする。  std::basic_regexCharT （正規表現） std::regex_match() （全体マッチ判定） std::regex_search() （部分マッチ判定） std::regex_iteratorIterator （部分マッチ結果の列挙） std::regex_token_iteratorIterator （部分マッチ結果のサブマッチの列挙）  これら以外に文字列の置換機能もあるがそれについては割愛する。 std::basic_regexCharT （正規表現） 正規表現はstd::basic_regexクラステンプレートを使って表現する。 std::regex re{R&quot;(abc (\d\d).(\d\d))&quot;};  std::regex は std::basic_regexchar の別名であり、通常はこれを使うだろう。 「R&quot;(...)&quot;」部分は生文字リテラルであり、正規表現を書く際にはこの記法が役に立つ。 使える正規表現はデフォルトではECMA..</description>
<content:encoded><![CDATA[<h3>C++の正規表現メモ</h3><p><a href="https://cpprefjp.github.io/reference/regex.html">&lt;regex&gt;</a>の使い方をメモ。</p>
<p>正規表現オブジェクトとそれを使った文字列に対するマッチ処理について以下をメモする。</p>
<ul>
<li>std::basic_regex&lt;CharT&gt; （正規表現）</li>
<li>std::regex_match() （全体マッチ判定）</li>
<li>std::regex_search() （部分マッチ判定）</li>
<li>std::regex_iterator&lt;Iterator&gt; （部分マッチ結果の列挙）</li>
<li>std::regex_token_iterator&lt;Iterator&gt; （部分マッチ結果のサブマッチの列挙）</li>
</ul>
<p>これら以外に文字列の置換機能もあるがそれについては割愛する。</p>
<h4>std::basic_regex&lt;CharT&gt; （正規表現）</h4>
<p>正規表現は<a href="https://cpprefjp.github.io/reference/regex/basic_regex.html">std::basic_regexクラステンプレート</a>を使って表現する。</p>
<pre>std::regex re{R"(abc (\d\d).(\d\d))"};</pre>
<ul>
<li>std::regex は std::basic_regex&lt;char&gt; の別名であり、通常はこれを使うだろう。</li>
<li>「R"(...)"」部分は<a href="https://cpprefjp.github.io/lang/cpp11/raw_string_literals.html">生文字リテラル</a>であり、正規表現を書く際にはこの記法が役に立つ。</li>
<li>使える正規表現はデフォルトでは<a href="https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Regular_expressions">ECMAScript</a>互換。コンストラクタで構文を切り替えることもできる。</li>
<li>ひらがななどの非ASCII文字のサポートは期待できない模様（よくわかっていない）。</li>
</ul>
<h4>std::regex_match() （全体マッチ判定）</h4>
<p><a href="https://cpprefjp.github.io/reference/regex/regex_match.html">std::regex_match()</a>を使うことで、文字列全体が正規表現にマッチするかどうかを判定できる。</p>
<pre>void regex_match1()
{
    static const std::regex re{R"(\w+ \d+/\d+/\d+)"};
    const bool is_matched1 = std::regex_match("hello 2025/01/02", re);
    const bool is_matched2 = std::regex_match("hello world 2025/01/02", re);

    std::cout &lt;&lt; "regex_match1" &lt;&lt; std::endl;
    std::cout &lt;&lt; is_matched1 &lt;&lt; std::endl; //=&gt; 1
    std::cout &lt;&lt; is_matched2 &lt;&lt; std::endl; //=&gt; 0
}</pre>
<ul>
<li>文字列の一部分ではなく全体がマッチする必要があることに注意。</li>
</ul>
<p>regex_match()の引数に<a href="https://cpprefjp.github.io/reference/regex/match_results.html">std::match_results</a>オブジェクトを指定すると、正規表現で指定したキャプチャグループ部分にマッチした内容を参照することができる。</p>
<pre>void regex_match2()
{
    static const std::regex re{R"(\w+ (\d+)/(\d+)/(\d+))"};
    std::cmatch m;
    const bool is_matched = std::regex_match("hello 2025/01/02", m, re);

    std::cout &lt;&lt; "regex_match2" &lt;&lt; std::endl;
    std::cout &lt;&lt; is_matched &lt;&lt; std::endl; //=&gt; 1
    std::cout &lt;&lt; m[0] &lt;&lt; std::endl;       //=&gt; hello 2025/01/02
    std::cout &lt;&lt; m[1] &lt;&lt; std::endl;       //=&gt; 2025
    std::cout &lt;&lt; m[2] &lt;&lt; std::endl;       //=&gt; 01
    std::cout &lt;&lt; m[3] &lt;&lt; std::endl;       //=&gt; 02
}</pre>
<ul>
<li>std::cmatch は std::match_results&lt;const char*&gt; の別名。<ul>
<li>std::match_resultsクラステンプレートにはこのようなエイリアスがいくつか定義されている。が、std::string_viewを使う場合は std::match_results&lt;std::string_view::const_iterator&gt; などとしないといけないようだ。</li>
</ul></li>
<li>上記の m[0] などで得られるのは単なる文字列ではなく<a href="https://cpprefjp.github.io/reference/regex/sub_match.html">std::sub_match</a>オブジェクト。std::match_results は std::sub_match を要素に持つコンテナだと考えることができる。</li>
</ul>
<h4>std::regex_search() （部分マッチ判定）</h4>
<p><a href="https://cpprefjp.github.io/reference/regex/regex_search.html">std::regex_search()</a>を使うことで、文字列が正規表現にマッチする部分を持つかどうかを判定できる。</p>
<pre>void regex_search()
{
    static const std::regex re{R"(\w+ (\d+)/(\d+)/(\d+))"};
    std::cmatch m;
    const bool is_matched = std::regex_search("hello world 2025/01/02", m, re);

    std::cout &lt;&lt; "regex_search" &lt;&lt; std::endl;
    std::cout &lt;&lt; is_matched &lt;&lt; std::endl; //=&gt; 1
    std::cout &lt;&lt; m[0] &lt;&lt; std::endl;       //=&gt; world 2025/01/02
    std::cout &lt;&lt; m[1] &lt;&lt; std::endl;       //=&gt; 2025
    std::cout &lt;&lt; m[2] &lt;&lt; std::endl;       //=&gt; 01
    std::cout &lt;&lt; m[3] &lt;&lt; std::endl;       //=&gt; 02
}</pre>
<h4>std::regex_iterator&lt;Iterator&gt; （部分マッチ結果の列挙）</h4>
<p><a href="https://cpprefjp.github.io/reference/regex/regex_iterator.html">std::regex_iteratorクラステンプレート</a>を使うと、std::regex_search()を繰り返し呼び出し、それぞれの呼び出しに対するマッチ結果（std::match_results オブジェクト）を順番に得ることができる。</p>
<pre>void regex_iterator()
{
    static const std::regex re{R"(\w+ (\d+)/(\d+)/(\d+))"};
    const std::string s = "hello world 2025/01/02 japan 1/2/3/4/5/6/7/8/9";

    std::cout &lt;&lt; "regex_iterator" &lt;&lt; std::endl;
    for (std::sregex_iterator it{s.begin(), s.end(), re}, end;
        it != end;
        ++it)
    {
        std::cout &lt;&lt; (*it)[0] &lt;&lt; std::endl;
        std::cout &lt;&lt; (*it)[1] &lt;&lt; std::endl;
        std::cout &lt;&lt; (*it)[2] &lt;&lt; std::endl;
        std::cout &lt;&lt; (*it)[3] &lt;&lt; std::endl;
        std::cout &lt;&lt; "---" &lt;&lt; std::endl;
    }
}</pre>
<pre>regex_iterator
world 2025/01/02
2025
01
02
---
japan 1/2/3
1
2
3
---</pre>
<ul>
<li>sregex_iterator は regex_iterator&lt;string::const_iterator&gt; の別名。</li>
</ul>
<h4>std::regex_token_iterator&lt;Iterator&gt; （部分マッチ結果のサブマッチの列挙）</h4>
<p><a href="https://cpprefjp.github.io/reference/regex/regex_token_iterator.html">std::regex_token_iteratorクラステンプレート</a>は regex_iterator に似ているが、マッチ結果ではなくサブマッチ（std::sub_match オブジェクト）の内容を直接得られるところが違う。</p>
<pre>void regex_token_iterator()
{
    static const std::regex re{R"(\w+ (\d+)/(\d+)/(\d+))"};
    const std::string s = "hello world 2025/01/02 japan 1/2/3/4/5/6/7/8/9";

    std::cout &lt;&lt; "regex_token_iterator" &lt;&lt; std::endl;
    for (std::sregex_token_iterator it{s.begin(), s.end(), re, {1, 3}}, end;
        it != end;
        ++it)
    {
        std::cout &lt;&lt; *it &lt;&lt; std::endl;
    }
}</pre>
<pre>regex_token_iterator
2025
02
1
3</pre>
<ul>
<li>sregex_token_iterator は regex_token_iterator&lt;string::const_iterator&gt; の別名。</li>
<li>コンストラクタの4番目の引数で、何番目のサブマッチを列挙するかを指定する。ここに -1 を指定することでマッチしなかった部分を列挙することもできる。</li>
</ul>
<p><a href="http://ogawa.s18.xrea.com/tdiary/20250220.html#c">ツッコミを入れる</a></p>]]></content:encoded>
</item>
<item rdf:about="http://ogawa.s18.xrea.com/tdiary/20250116p01.html#p01">
<link>http://ogawa.s18.xrea.com/tdiary/20250116p01.html#p01</link>
<dc:date>2025-01-16T16:06:00+09:00</dc:date>
<title>WindowsでのデフォルトのPython環境</title>
<dc:creator>OGAWA KenIchi</dc:creator>
<dc:subject>windows</dc:subject>
<dc:subject>python</dc:subject>
<description>Windows 11で使用するPythonはMicrosoft Storeからインストールするようにしている。 Microsoft StoreでのPythonはバージョン毎に別のアプリとして公開されているが、複数のバージョンをインストールしたときに使われるPythonをどう制御すればいいのか。 Windows上でのデフォルトのPython環境 コマンドプロンプト上などでpythonコマンドを実行したときに使われるバージョンは、次で表示される設定画面から制御できるようだ。  ［設定］→［アプリ］→［アプリの詳細設定］→［アプリ実行エイリアス］  この画面の一覧の中で、デフォルトで使用したいバージョンのPythonの設定をすべて「オン」にすることで、そのバージョンがデフォルトで使われるようになる（コマンド毎にオンにするバージョンを変えることもできるが、それは混乱の元にしかならないだろう）。  Visual Studio上でのデフォルトのPython環境 Visual Studio 2022でPythonプロジェクトを作成したとき、プロジェクトで明示的にバージョン指定をしないとPytho..</description>
<content:encoded><![CDATA[<h3>WindowsでのデフォルトのPython環境</h3><p>Windows 11で使用するPythonはMicrosoft Storeからインストールするようにしている。</p>
<p>Microsoft StoreでのPythonはバージョン毎に別のアプリとして公開されているが、複数のバージョンをインストールしたときに使われるPythonをどう制御すればいいのか。</p>
<h4>Windows上でのデフォルトのPython環境</h4>
<p>コマンドプロンプト上などでpythonコマンドを実行したときに使われるバージョンは、次で表示される設定画面から制御できるようだ。</p>
<ul>
<li>［設定］→［アプリ］→［アプリの詳細設定］→［アプリ実行エイリアス］</li>
</ul>
<p>この画面の一覧の中で、デフォルトで使用したいバージョンのPythonの設定をすべて「オン」にすることで、そのバージョンがデフォルトで使われるようになる（コマンド毎にオンにするバージョンを変えることもできるが、それは混乱の元にしかならないだろう）。</p>
<p><img class="photo" src="http://ogawa.s18.xrea.com/images/20250116_0.png" alt="アプリ実行エイリアス" title="アプリ実行エイリアス" width="466" height="438"></p>
<h4>Visual Studio上でのデフォルトのPython環境</h4>
<p>Visual Studio 2022でPythonプロジェクトを作成したとき、プロジェクトで明示的にバージョン指定をしないとPython環境として「グローバル デフォルト」と表示されるものが使われるようだ。そして、これは上述したWindows上でのデフォルト環境を変更してもそれに追従しなかった。</p>
<p>Visual Studio上のグローバルデフォルトは、次で表示されるPython環境ウィンドウで制御できるようだ。</p>
<ul>
<li>［ツール］→［Python］→［Python環境］</li>
</ul>
<p>この画面の環境の一覧にて太字で表示されているものがグローバルデフォルトな模様。</p>
<p>これを変更するには、一覧で非グローバルデフォルト環境を選択し、「概要」欄の「これを新しいプロジェクトに対する既定の環境にする」をクリックすればよい。その後でPythonプロジェクトを開き直すと、Python環境として表示されるPythonのバージョンが変わった。</p>
<p><img class="photo" src="http://ogawa.s18.xrea.com/images/20250116_1.png" alt="Python環境ウィンドウ" title="Python環境ウィンドウ" width="355" height="319"></p>
<p><a href="http://ogawa.s18.xrea.com/tdiary/20250116.html#c">ツッコミを入れる</a></p>]]></content:encoded>
</item>
<item rdf:about="http://ogawa.s18.xrea.com/tdiary/20241126p01.html#p01">
<link>http://ogawa.s18.xrea.com/tdiary/20241126p01.html#p01</link>
<dc:date>2024-11-26T13:59:18+09:00</dc:date>
<title>AndroidのBLEを使った通信で512バイトを超える送信データが破棄される</title>
<dc:creator>OGAWA KenIchi</dc:creator>
<dc:subject>android</dc:subject>
<dc:subject>net</dc:subject>
<description>AndroidのBluetooth APIを使ったBLE通信で、送信データが破棄されれ送信されないことがあったので調べた。 原因はAndroid 13と14での動作の変更だった。 Android 13での変更 ちゃんとした資料は見つけられなかったが、Android 13から512バイトを超えるデータを送受信できなくなったというissueがちらほら見つかる。これなど。 次の変更が原因のようだ。  Set maximum attribute value to defined by spec Core 5.3 (android.googlesource.com)  この変更は、次に示すBluetoothの仕様でattribute valueの最大長が512バイトに制限されていることへ準じるために行われている。  3.2.9. Long attribute values (Bluetooth Core Specification 6.0)  The maximum length of an attribute value shall be 512 octets.  どうも、Bluetoot..</description>
<content:encoded><![CDATA[<h3>AndroidのBLEを使った通信で512バイトを超える送信データが破棄される</h3><p>Androidの<a href="https://developer.android.com/develop/connectivity/bluetooth">Bluetooth API</a>を使ったBLE通信で、送信データが破棄されれ送信されないことがあったので調べた。</p>
<p>原因はAndroid 13と14での動作の変更だった。</p>
<h4>Android 13での変更</h4>
<p>ちゃんとした資料は見つけられなかったが、Android 13から512バイトを超えるデータを送受信できなくなったというissueがちらほら見つかる。<a href="https://github.com/dariuszseweryn/RxAndroidBle/pull/808">これ</a>など。</p>
<p>次の変更が原因のようだ。</p>
<ul>
<li><a href="https://android.googlesource.com/platform/packages/modules/Bluetooth/+/f25b742ca33a4da6fb3d4c0367c3aa4a3dd04874%5E%21/#F0">Set maximum attribute value to defined by spec Core 5.3</a> (android.googlesource.com)</li>
</ul>
<p>この変更は、次に示すBluetoothの仕様でattribute valueの最大長が512バイトに制限されていることへ準じるために行われている。</p>
<ul>
<li><a href="https://www.bluetooth.com/wp-content/uploads/Files/Specification/HTML/Core-60/out/en/host/attribute-protocol--att-.html#UUID-2ba771de-5856-80be-fb28-b22d8a7ef545">3.2.9. Long attribute values</a> (Bluetooth Core Specification 6.0)</li>
</ul>
<blockquote><p>The maximum length of an attribute value shall be 512 octets.</p>
</blockquote>
<p>どうも、Bluetoothでデータ送信を行う際のデータ長の上限としてATT（<a href="https://www.bluetooth.com/wp-content/uploads/Files/Specification/HTML/Core-60/out/en/host/attribute-protocol--att-.html">Attribute Protocol</a>）のMTUサイズから3バイト引いた値を使うコードが多いようで（手元のコードもそうなっていた）、AndroidのATT MTUサイズは最大で517バイトになり得て、そうなったときに 517 - 3 = 515 バイトのデータを送信しようとしてしまい、その結果Android 12までであれば送信できていたのにAndroid 13になったら送信できなくなったという問題があるようだ。</p>
<h4>Android 14での変更</h4>
<p>Android 14では次の変更がされたとある。</p>
<ul>
<li><a href="https://developer.android.com/about/versions/14/behavior-changes-all#mtu-set-to-517">MTU is set to 517 for the first GATT client requesting an MTU</a> (Android Developers)</li>
</ul>
<p>これは、<a href="https://developer.android.com/reference/android/bluetooth/BluetoothGatt#requestMtu(int)">BluetoothGatt#requestMtu()</a>によりMTUサイズの拡張を要求したときに引数の値によらず常に517バイトが使われるということのようだ。</p>
<p>手元のコードでは、上述したAndroid 13での変更に対する対策として BluetoothGatt#requestMtu() で515を使うようになっていたが、この対策はAndroid 14以降では意味がなくなってしまうことになる。</p>
<h4>変更への対応方法</h4>
<p>送信データの長さの上限を (MTUサイズ - 3) とするだけでは問題があるということになる。</p>
<p>送信データの長さの上限は (min(512, MTUサイズ - 3)) とする必要がある。</p>
<h4>AndroidのATT MTUサイズの上限が517なのは何故か</h4>
<p>検索すると、AndroidのATT MTUサイズの上限は517ではなく515であるべきだと書いている人を見かける。これに対しては、次のissueにGoogleの人による説明があった。</p>
<ul>
<li><a href="https://issuetracker.google.com/issues/307234027#comment4">[BLE] - Incoming packets longer then 515 dropped after BluetoothGatt.requestMtu(517) used, even though remote peer supports MTU 517</a> (Android Public Tracker)</li>
</ul>
<blockquote><p>I would like to provide you more details about why 517 was chosen instead of 515:</p>
<p>The number 517 was selected because the GATT_MAX_MTU_SIZE constant was set to 517 since the initial drop of Android Bluetooths stack in 2012.</p>
<p>A deeper reason is that the Bluetooth Specification allows the maximum size of an ATT attribute to be 512 bytes and the largest command ATT_PREPARE_WRITE_REQ has 5 bytes of header. Hence 512 + 5 = 517.</p>
</blockquote>
<p><a href="http://ogawa.s18.xrea.com/tdiary/20241126.html#c">ツッコミを入れる</a></p>]]></content:encoded>
</item>
<item rdf:about="http://ogawa.s18.xrea.com/tdiary/20241008p01.html#p01">
<link>http://ogawa.s18.xrea.com/tdiary/20241008p01.html#p01</link>
<dc:date>2024-11-26T13:25:21+09:00</dc:date>
<title>SQLModelのdatetimeに対する動作</title>
<dc:creator>OGAWA KenIchi</dc:creator>
<dc:subject>python</dc:subject>
<description>どうしてそうなるのかは理解できていないが、事実をメモ。 SQLModel0.0.22を使っている。 datetime型のフィールドを持つモデルを定義して、そのmodel_validate_json()によりJSONからのでシリアライズを行ったときに次のようになる。  table=True無しで定義したモデルはdatetime型のフィールドにdatetime型の値が格納される table=True有りで定義したモデルはdatetime型のフィールドにstr型の値が格納されてしまう  検証コードと実行結果を示す。 from datetime import datetime  from sqlmodel import Field, SQLModel   class A(SQLModel):     id: int = Field(primary_key=True)     dt: datetime   class B(SQLModel, table=True):     id: int = Field(primary_key=True)     dt: datetime   json =..</description>
<content:encoded><![CDATA[<h3>SQLModelのdatetimeに対する動作</h3><p>どうしてそうなるのかは理解できていないが、事実をメモ。</p>
<p><a href="https://sqlmodel.tiangolo.com/">SQLModel</a>0.0.22を使っている。</p>
<p><a href="https://docs.python.org/ja/3/library/datetime.html#datetime-objects">datetime</a>型のフィールドを持つモデルを定義して、その<a href="https://docs.pydantic.dev/2.0/api/main/#pydantic.main.BaseModel.model_validate_json">model_validate_json()</a>によりJSONからのでシリアライズを行ったときに次のようになる。</p>
<ul>
<li>table=True無しで定義したモデルはdatetime型のフィールドにdatetime型の値が格納される</li>
<li>table=True有りで定義したモデルはdatetime型のフィールドにstr型の値が格納されてしまう</li>
</ul>
<p>検証コードと実行結果を示す。</p>
<pre>from datetime import datetime

from sqlmodel import Field, SQLModel


class A(SQLModel):
    id: int = Field(primary_key=True)
    dt: datetime


class B(SQLModel, table=True):
    id: int = Field(primary_key=True)
    dt: datetime


json = '{ "id": 1, "dt": "2024-01-02T03:45:06+09:00" }'
a = A.model_validate_json(json)
b = B.model_validate_json(json)
print(type(a.dt))
print(type(b.dt))</pre>
<pre>&lt;class 'datetime.datetime'&gt;
&lt;class 'str'&gt;</pre>
<h4>(追記)</h4>
<p>上記のようになるのは、データベースとして使用していたのがSQLiteで、<a href="https://www.sqlite.org/quirks.html#no_separate_datetime_datatype">SQLiteのdatetime型はtext型と同じ</a>だからのようだった。</p>
<p><a href="http://ogawa.s18.xrea.com/tdiary/20241008.html#c">ツッコミを入れる</a></p>]]></content:encoded>
</item>
<item rdf:about="http://ogawa.s18.xrea.com/tdiary/20241001p01.html#p01">
<link>http://ogawa.s18.xrea.com/tdiary/20241001p01.html#p01</link>
<dc:date>2024-10-01T12:18:25+09:00</dc:date>
<title>WSLにポートフォワードで外部からアクセス可能にする</title>
<dc:creator>OGAWA KenIchi</dc:creator>
<dc:subject>windows</dc:subject>
<dc:subject>net</dc:subject>
<dc:subject>howto</dc:subject>
<description>WSLに外部からSSHでアクセスできるようにする方法をメモ。なお、試した環境はWindows 11ではなくWindows 10。 WSL上でsshdを使えるようにする方法は割愛。WSLをホストしているWindowsからはWSLへSSH接続できるものとする。 ポートフォワーディング設定 Windowsホスト上でnetsh interface portproxyコマンドを使ってWSLへのポートフォワーディングを設定する。なお、この設定には管理者権限が必要。  netsh interface portproxy add v4tov4 `     listenport=22222 listenaddress=0.0.0.0 `     connectport=22 connectaddress=192.0.2.100 設定の確認はshow allサブコマンドでできる。  netsh interface portproxy show all  ipv4 をリッスンする:         ipv4 に接続する:  Address         Port        Address     ..</description>
<content:encoded><![CDATA[<h3>WSLにポートフォワードで外部からアクセス可能にする</h3><p>WSLに外部からSSHでアクセスできるようにする方法をメモ。なお、試した環境はWindows 11ではなくWindows 10。</p>
<p>WSL上でsshdを使えるようにする方法は割愛。WSLをホストしているWindowsからはWSLへSSH接続できるものとする。</p>
<h4>ポートフォワーディング設定</h4>
<p>Windowsホスト上で<a href="https://learn.microsoft.com/ja-jp/windows-server/networking/technologies/netsh/netsh-interface-portproxy">netsh interface portproxyコマンド</a>を使ってWSLへのポートフォワーディングを設定する。なお、この設定には管理者権限が必要。</p>
<pre>&gt; netsh interface portproxy add v4tov4 `
    listenport=22222 listenaddress=0.0.0.0 `
    connectport=22 connectaddress=192.0.2.100</pre>
<p>設定の確認はshow allサブコマンドでできる。</p>
<pre>&gt; netsh interface portproxy show all

ipv4 をリッスンする:         ipv4 に接続する:

Address         Port        Address         Port
--------------- ----------  --------------- ----------
0.0.0.0         22222       192.0.2.100     22</pre>
<p>これにより、Windowsホストのポート22222へのアクセスはWSL（のアドレスを仮に192.0.2.100とした）のポート22へ転送されるようになる。</p>
<h4>ファイアウォール設定</h4>
<p>上記に加え、Windowsのファイアウォール設定を変更しないとアクセスできなかった。ファイアウォールの設定は<a href="https://learn.microsoft.com/ja-jp/troubleshoot/windows-server/networking/netsh-advfirewall-firewall-control-firewall-behavior#command-example-2-enable-a-port">netsh advfirewallコマンド</a>を使って行える。こちらも要管理者権限。</p>
<pre>&gt; netsh advfirewall firewall add rule `
    name="WSL-SSH" `
    protocol=TCP `
    localport=22222 `
    dir=in `
    action=allow</pre>
<p>設定の確認は次の通り。</p>
<pre>&gt; netsh advfirewall firewall show rule name=WSL-SSH

規則名:                               WSL-SSH
----------------------------------------------------------------------
有効:                                 はい
方向:                                 入力
プロファイル:                         ドメイン,プライベート,パブリック
グループ:
ローカル IP:                          任意
リモート IP:                          任意
プロトコル:                           TCP
ローカル ポート:                      22222
リモート ポート:                      任意
エッジ トラバーサル:                  いいえ
操作:                                 許可
OK</pre>
<p>以上の設定で外部からWSLのSSHへのアクセスができるようになった。</p>
<h4>設定の削除方法</h4>
<p>ファイアウォール設定とポートフォワード設定を削除して元に戻すには次のようにする。</p>
<pre>&gt; netsh advfirewall firewall delete rule name=WSL-SSH

1 規則を削除しました。
OK</pre>
<pre>&gt; netsh interface portproxy delete v4tov4 listenport=22222 listenaddress=0.0.0.0</pre>
<h4>NetNat系コマンドとの違い</h4>
<p><a href="http://ogawa.s18.xrea.com/tdiary/20240402p01.html#p01">以前</a>にHyper-V上のVMへ外部からアクセスする方法をメモしていて、このときはnetshではなく<a href="https://learn.microsoft.com/ja-jp/powershell/module/netnat/add-netnatstaticmapping">Add-NetNatStaticMapping</a>コマンドなどを使っていた。</p>
<p>WSLについても同じようにしてできるのかもしれないが、netshを使うと仮想スイッチの設定を気にしなくていいのでこちらの方がWSL向きな気がする。</p>
<p>違いは理解できていない。</p>
<p><a href="http://ogawa.s18.xrea.com/tdiary/20241001.html#c">ツッコミを入れる</a></p>]]></content:encoded>
</item>
<item rdf:about="http://ogawa.s18.xrea.com/tdiary/20240927p01.html#p01">
<link>http://ogawa.s18.xrea.com/tdiary/20240927p01.html#p01</link>
<dc:date>2024-09-27T13:23:22+09:00</dc:date>
<title>作成するWheelファイルのファイル名</title>
<dc:creator>OGAWA KenIchi</dc:creator>
<dc:subject>python</dc:subject>
<description>昨日の例で作られるWheelファイルのファイル名は  oreore-0.1.0-py3-none-any.whl  になる。これの「py3-none-any」の部分はPlatform compatibility tagsと呼ばれ、  python tag abi tag platform tag  を並べたものになっている。 Wheelが特定のPythonバージョンや特定のプラットフォームを要求する場合、これらのタグを適切に指定する必要があるが、作成するファイル名を制御するにはどうすればいいのか？ 方法1: buildの--config--settingオプションを使用する 昨日の例ではWheelの作成にpipを使用したが、pipでファイル名を制御する方法はわからなかった。buildを使えばある程度制御できて、昨日の環境で次のコマンド $ python -m build . --wheel \   --config-setting=&quot;--build-option=--python-tag hello --plat-name world&quot; を実行すると  oreore-0.1.0-h..</description>
<content:encoded><![CDATA[<h3>作成するWheelファイルのファイル名</h3><p><a href="http://ogawa.s18.xrea.com/tdiary/20240926p01.html#p01">昨日の例</a>で作られるWheelファイルのファイル名は</p>
<ul>
<li>oreore-0.1.0-py3-none-any.whl</li>
</ul>
<p>になる。これの「py3-none-any」の部分は<a href="https://packaging.python.org/ja/latest/specifications/platform-compatibility-tags/">Platform compatibility tags</a>と呼ばれ、</p>
<ul>
<li>python tag</li>
<li>abi tag</li>
<li>platform tag</li>
</ul>
<p>を並べたものになっている。</p>
<p>Wheelが特定のPythonバージョンや特定のプラットフォームを要求する場合、これらのタグを適切に指定する必要があるが、作成するファイル名を制御するにはどうすればいいのか？</p>
<h4>方法1: buildの--config--settingオプションを使用する</h4>
<p>昨日の例ではWheelの作成にpipを使用したが、pipでファイル名を制御する方法はわからなかった。<a href="https://build.pypa.io/">build</a>を使えばある程度制御できて、昨日の環境で次のコマンド</p>
<pre>$ python -m build . --wheel \
  --config-setting="--build-option=--python-tag hello --plat-name world"</pre>
<p>を実行すると</p>
<ul>
<li>oreore-0.1.0-hello-none-world.whl</li>
</ul>
<p>というファイル名のWheelが作られる。</p>
<p>abi tagを指定する方法は分からなかった。また、確認したビルドバックエンドはSetuptoolsだけで、別のバックエンドを使う場合は別の指定方法になると思われる。</p>
<h4>方法2: wheel tagsコマンドでファイル名を変更する</h4>
<p><a href="https://pypi.org/project/wheel/">wheel</a>というパッケージがあり、これをインストールすると<a href="https://wheel.readthedocs.io/en/stable/reference/wheel_tags.html">wheel tags</a>というコマンドを使えるようになって、これでWheelのファイル名の変更が簡単にできる。</p>
<pre>$ python -m wheel tags oreore-0.1.0-py3-none-any.whl \
  --python-tag=hello --platform-tag=world --abi-tag=abc</pre>
<p>上のコマンドを実行すると次のファイル名のWheelが作られる。abi tagも指定できる。</p>
<ul>
<li>oreore-0.1.0-hello-abc-world.whl</li>
</ul>
<h4>適切なタグ名を取得する</h4>
<p>各タグの名前を取得するのには、<a href="https://packaging.pypa.io/en/stable/">packaging</a>というパッケージが使える。</p>
<p>例えば、動作しているPython環境に一番適合したタグ名は packaging.tags.sys_tags() により取得できる。</p>
<pre>&gt;&gt;&gt; import packaging.tags
&gt;&gt;&gt; print(next(packaging.tags.sys_tags()))
cp312-cp312-manylinux_2_35_x86_64
&gt;&gt;&gt;</pre>
<p><a href="http://ogawa.s18.xrea.com/tdiary/20240927.html#c">ツッコミを入れる</a></p>]]></content:encoded>
</item>
<item rdf:about="http://ogawa.s18.xrea.com/tdiary/20240926p01.html#p01">
<link>http://ogawa.s18.xrea.com/tdiary/20240926p01.html#p01</link>
<dc:date>2024-09-26T17:05:14+09:00</dc:date>
<title>Pythonにおけるパッケージング(2)</title>
<dc:creator>OGAWA KenIchi</dc:creator>
<dc:subject>python</dc:subject>
<description>7年前にPython 2でパッケージ化する方法をメモしていた。少し前にPython 3でパッケージを作る方法を調べたので、忘れないようにメモしておく。 情報源が https://packaging.python.org/ であることは変わっていない。日本語版もある。 pyproject.tomlを用意 現代ではsetup.pyの代わりにpyproject.tomlを使う。ここに次のような内容を設定する。 [project] name = &quot;oreore&quot; version = &quot;0.1.0&quot;  [build-system] requires = [&quot;setuptools&quot;] build-backend = &quot;setuptools.build_meta&quot; [build-system] に指定可能なツールが色々あって悩みどころだが、本記事ではSetuptoolsを使う。 Pythonのコードを用意 以前と同じ構成のコードを用意する。ただし、コードは「src/パッケージ名」というディレクトリの下に置くのが標準的なようなのでそのようにする（参考:src レイアウト対フラットレイアウト）。 $..</description>
<content:encoded><![CDATA[<h3>Pythonにおけるパッケージング(2)</h3><p><a href="http://ogawa.s18.xrea.com/tdiary/20170414p01.html#p01">7年前</a>にPython 2でパッケージ化する方法をメモしていた。少し前にPython 3でパッケージを作る方法を調べたので、忘れないようにメモしておく。</p>
<p>情報源が <a href="https://packaging.python.org/">https://packaging.python.org/</a> であることは変わっていない。<a href="https://packaging.python.org/ja/latest/">日本語版</a>もある。</p>
<h4>pyproject.tomlを用意</h4>
<p>現代ではsetup.pyの代わりに<a href="https://packaging.python.org/ja/latest/guides/writing-pyproject-toml/#writing-pyproject-toml">pyproject.toml</a>を使う。ここに次のような内容を設定する。</p>
<pre>[project]
name = "oreore"
version = "0.1.0"

[build-system]
requires = ["setuptools"]
build-backend = "setuptools.build_meta"</pre>
<p>[build-system] に指定可能なツールが色々あって悩みどころだが、本記事では<a href="https://setuptools.pypa.io/">Setuptools</a>を使う。</p>
<h4>Pythonのコードを用意</h4>
<p>以前と同じ構成のコードを用意する。ただし、コードは「src/パッケージ名」というディレクトリの下に置くのが標準的なようなのでそのようにする（参考:<a href="https://packaging.python.org/ja/latest/discussions/src-layout-vs-flat-layout/">src レイアウト対フラットレイアウト</a>）。</p>
<pre>$ tree . --noreport
.
├── pyproject.toml
└── src
    └── oreore
        ├── hello
        │   ├── __init__.py
        │   └── hello.py
        └── world
            ├── __init__.py
            └── world.py</pre>
<p>前回同様hello.pyを実行可能スクリプトと認識させたかったが、pyproject.tomlでそのような指定をする方法は無さそうだった。代わりに、hello.pyの関数を呼び出す実行可能スクリプトを自動生成させることはできて、そのためにはpyproject.tomlに次のような設定を追加すればよい（参考:<a href="https://setuptools.pypa.io/en/latest/userguide/entry_point.html">Entry Points</a>）。</p>
<pre>[project.scripts]
hellohello = "oreore.hello.hello:hello"</pre>
<p>これにより、oreore/hello/hello.py の関数hello()を呼び出すスクリプト hellohello が、oreoreパッケージのインストール時に作られるようになる。</p>
<h4>Wheelファイルを作る</h4>
<p>以上の準備により、Wheelファイルを作成できるようになる。</p>
<p>Wheelファイルを作成するには<a href="https://build.pypa.io/">build</a>を使うことが標準的なようだ。ただし、別途インストールが必要。pipにも<a href="https://pip.pypa.io/en/stable/cli/pip_wheel/">Wheelの作成機能</a>があるので、ここではbuildではなくpipを使う。</p>
<pre>$ python -m pip wheel --no-deps .</pre>
<p>これにより、カレントディレクトリに oreore-0.1.0-py3-none-any.whl が作られる。</p>
<p>pip wheelの注意点として、buildというディレクトリが作られその中にビルド時のソースコードが残るという点がある。これが次のpip wheel実行時にも参照されるので、例えばhello.pyをhello2.pyに改名してpip wheelを実行し直すとWheelファイル内に（本来は含まれるべきでない）hello.pyが含まれてしまう（pipのバージョン24.2で確認）。</p>
<p>なので、pip wheelの実行前にはbuildディレクトリは削除した方がよさそう。buildにはこの問題はなさそうだったので、pip wheelではなく素直にbuildを使う方がいいのかもしれない（この段落はディレクトリ名のbuildとツール名のbuildが混在していて分かりにくい…）。</p>
<p><a href="http://ogawa.s18.xrea.com/tdiary/20240926.html#c">ツッコミを入れる</a></p>]]></content:encoded>
</item>
<item rdf:about="http://ogawa.s18.xrea.com/tdiary/20240924p01.html#p01">
<link>http://ogawa.s18.xrea.com/tdiary/20240924p01.html#p01</link>
<dc:date>2024-09-25T01:33:33+09:00</dc:date>
<title>git submodule statusで表示される3番目の項目の内容</title>
<dc:creator>OGAWA KenIchi</dc:creator>
<dc:subject>dev</dc:subject>
<description>submoduleを使用しているGitリポジトリにおいて「git submodule」を実行すると次のように表示される。  a2e59f0e7065404b44dfe92a28aca47ba1378dc4 submodule/pybind11 (v2.11.0-182-ga2e59f0e) このサブモジュールはpybind11のタグv2.13.6を指すようにしているのだが、括弧内にはv2.11.0と表示されていてモヤモヤする。 この最後の括弧内に表示される内容が何なのかについて調べたのでメモ。手元のGitのバージョンは2.44.0。 括弧内に表示される内容 「git submodule」で何が表示されるのかについては、git submoduleのマニュアルに With no arguments, shows the status of existing submodules.  とある。「status」の説明は無いが、「git submodule status」を実行したときと同じ内容が出力されるという理解でいいのだと思う。 同マニュアルのstatusサブコマンドの説明には次のよう..</description>
<content:encoded><![CDATA[<h3>git submodule statusで表示される3番目の項目の内容</h3><p><a href="https://git-scm.com/book/ja/v2/Git-%E3%81%AE%E3%81%95%E3%81%BE%E3%81%96%E3%81%BE%E3%81%AA%E3%83%84%E3%83%BC%E3%83%AB-%E3%82%B5%E3%83%96%E3%83%A2%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%AB">submodule</a>を使用しているGitリポジトリにおいて「git submodule」を実行すると次のように表示される。</p>
<pre> a2e59f0e7065404b44dfe92a28aca47ba1378dc4 submodule/pybind11 (v2.11.0-182-ga2e59f0e)</pre>
<p>このサブモジュールは<a href="https://github.com/pybind/pybind11">pybind11</a>のタグv2.13.6を指すようにしているのだが、括弧内にはv2.11.0と表示されていてモヤモヤする。</p>
<p>この最後の括弧内に表示される内容が何なのかについて調べたのでメモ。手元のGitのバージョンは2.44.0。</p>
<h4>括弧内に表示される内容</h4>
<p>「git submodule」で何が表示されるのかについては、<a href="https://git-scm.com/docs/git-submodule#_commands">git submoduleのマニュアル</a>に</p>
<blockquote><p>With no arguments, shows the status of existing submodules.</p>
</blockquote>
<p>とある。「status」の説明は無いが、「git submodule status」を実行したときと同じ内容が出力されるという理解でいいのだと思う。</p>
<p>同マニュアルのstatusサブコマンドの説明には次のようにある。</p>
<blockquote><p>Show the status of the submodules. This will print the SHA-1 of the currently checked out commit for each submodule, along with the submodule path and the output of git describe for the SHA-1.</p>
</blockquote>
<p>出力の括弧内には「git describe」の出力結果が表示されるということのようだ。</p>
<p>実際、submodule/pybind11 にて「git describe」を実行すると「v2.11.0-182-ga2e59f0e」と表示された。</p>
<h4>git describeの出力</h4>
<p>git describeは何を表示しているのか。</p>
<p><a href="https://git-scm.com/docs/git-describe#_description">git describeのマニュアル</a>には次のようにあった。</p>
<blockquote><p>The command finds the most recent tag that is reachable from a commit. If the tag points to the commit, then only the tag is shown. Otherwise, it suffixes the tag name with the number of additional commits on top of the tagged object and the abbreviated object name of the most recent commit.</p>
</blockquote>
<p>A-B-C という形式の出力で次のようになると考えてよさそう。</p>
<ul>
<li>Aの部分は、カレントのコミットから過去に辿って見つかる最初のタグの名前</li>
<li>Bの部分は、Aのタグからカレントのコミットまでにあるコミットの数</li>
<li>Cの部分は、カレントのコミットの名前の先頭部分</li>
</ul>
<p>でもおかしい。手元のサブモジュールはタグv2.13.6を指しているのにAの部分がv2.11.0になっている。これは何故か。</p>
<p>マニュアルの続きに次のようにあった。</p>
<blockquote><p>By default (without --all or --tags) git describe only shows annotated tags.</p>
</blockquote>
<p>Gitのタグには<a href="https://git-scm.com/book/ja/v2/Git-%E3%81%AE%E5%9F%BA%E6%9C%AC-%E3%82%BF%E3%82%B0#_%E3%82%BF%E3%82%B0%E3%81%AE%E4%BD%9C%E6%88%90">lightweightとannotatedの2種類</a>があり、オプションなしのgit describeがAの部分に出力するのはannotatedなタグだけのようである。</p>
<p>なので、pybind11のタグv2.13.6はlightweightタグであり、それより古い最新のannotatedタグがv2.11.0だからあのような出力になっているということになる。</p>
<h4>タグの種類の確認方法</h4>
<p>タグがlightweightなのかannotatedなのかを調べる明快な方法はわからなかったが、<a href="https://git-scm.com/docs/git-cat-file">git cat-file</a>の -t オプションを使うのがいいように思う。</p>
<p>このコマンドは指定したオブジェクトのタイプを出力するが、lightweightタグのタイプはcommit、annotatedタグのタイプはtagになるのでこれで区別できる。</p>
<pre>$ git cat-file -t v2.13.6
commit

$ git cat-file -t v2.11.0
tag</pre>
<p>また、<a href="https://git-scm.com/docs/git-for-each-ref">git for-each-ref</a>を使うと全てのタグについてタイプを確認できる。</p>
<pre>$ git for-each-ref refs/tags
（省略）
8d8aecf4a5579c0e51d07fb93411aa120ae0360c tag    refs/tags/v2.11.0
0630807c3070287c716f6be3eacb00b8816b4215 tag    refs/tags/v2.11.1
95d943ae0ebdf609bbd650d119fda539509929b6 commit refs/tags/v2.11.2
3e9dfa2866941655c56877882565e7577de6fc7b commit refs/tags/v2.12.0
2e0815278cb899b20870a67ca8205996ef47e70f commit refs/tags/v2.12.1
0c69e1eb2177fa8f8580632c7b1f97fdb606ce8f commit refs/tags/v2.13.0
941f45bcb51457884fa1afd6e24a67377d70f75c commit refs/tags/v2.13.1
07f30430d4186c2712761f1ffaea50ede63f2b2b commit refs/tags/v2.13.2
bd67643652d3800837f1f41549a2a5adbaa3fafe commit refs/tags/v2.13.3
c6239a8a1b6871cc0fb5f7af885a02ffd1349f9d commit refs/tags/v2.13.4
7c33cdc2d39c7b99a122579f53bc94c8eb3332ff commit refs/tags/v2.13.5
a2e59f0e7065404b44dfe92a28aca47ba1378dc4 commit refs/tags/v2.13.6
（省略）</pre>
<p>これを見ると、v2.11.1もannotatedタグなのでおかしいなと思ったが、どうやらタグv2.11.1はタグv2.13.6の先祖には位置していないようだった。どうしてそうなっているのかはわからないが。</p>
<p><a href="http://ogawa.s18.xrea.com/tdiary/20240924.html#c">ツッコミを入れる</a></p>]]></content:encoded>
</item>
</rdf:RDF>
