NixOSでmatplotlibを使いグラフを表示する - uvを使う & 共有ライブラリとoverlayの話

要約 ここではNixOSとPythonとmatplotlibでグラフを出力するための方法を記載する。 グラフを表示するケースとして以下の2つのケースを考える。 (パターン1)NixだけでPythonのパッケージ管理をする方法 (パターン2)astral-sh/uvでPythonのパッケージ管理をする方法 前者はあっさり終わるが、後者はすんなり動かないので工夫が必要。具体的には以下の工夫がいる。 共有ライブラリのパスが解決できずエラーになる: uvが外部から持ってきたライブラリにプリコンパイルされたCの共有ライブラリファイルがあるため。LD_LIBRARY_PATH の指定をする必要がある tkinterのモジュールが解決できず、グラフが出力されない:nixpkgsに入っているPythonのデフォルトにはtkinterがついてないため。overlayとoverrideを使い、tkinter入りのPythonを用意する NixOSでほかのLinuxディストリビューションと同じようなことをしようとするとひと手間必要という良い例かも。 なお、今回紹介するのはあくまで NixOS 上での例である。例えばNixOSでないほかのOSでNixパッケージマネージャだけ導入しているようなケースでは、(環境によるが)パターン2は特に工夫なく動くかもしれない。 また、今回使うuvは0.4.8である。uvは開発が早いので、数か月後にはこの記事通りに動かなくなってるかも。 1 2 [bombrary@nixos:~/example]$ uv --version uv 0.4.8 (パターン1)Nixを用いたパッケージ管理例 はじめに、flake.nix ファイルを作る。 1 2 [bombrary@nixos:~/example]$ nix flake init wrote: /home/bombrary/example/flake.nix 以下のような flake.nix を書く。python.withPackagesを使うことで、特定のパッケージが入ったPythonを作ることができる(イメージ的には、venvと同じものを /nix/store/ で管理する感じ)。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 { description = "A very basic flake"; inputs = { nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable"; }; outputs = { self, nixpkgs }: let pkgs = nixpkgs.legacyPackages.x86_64-linux; my-python = pkgs.python312.withPackages (python-pkgs: [ python-pkgs.matplotlib ]); in { devShells.x86_64-linux.default = pkgs.mkShell { packages = with pkgs; [ my-python ]; }; }; } nix develop して、上記で定義したPython環境が使える状態にする。 ...

2024-09-16 · (updated 2024-09-16) · 8 min · 1533 words

Nixでハッシュ関係の処理をPythonで実装してみる

前回の記事でstore pathを手で計算する方法を見てきたが、output hashの計算については手で計算するのが無理だった。これをPythonスクリプトで実装するとどうなるかをやってみた。 ゴールとしてはoutput hashを計算するコードを実装することであるが、 Nix32表現の計算とtruncateオプションの計算はそれにあたって必要なので実装した おまけでderivation hashとsource hashの計算も実装した なお、今回のコードについて Nix 2.21.1を参考に作っている すべてのパターンは網羅できていない可能性が高い(特にoutput hashの計算方法) Nixのいくつかの処理をPythonで実装してみるのコードを一部使って実装する である。また、コードの実行例にあたって、前回の記事のderivationの準備にしたがってsampleのderivationが準備されているものとする。 Nix32表現の計算 Nix32の計算はlibutil/hash.ccで行われている。 以下の並びのビット列があるとする(見やすさのため8bitごとに縦棒で区切ってある)。 1 b07 b06 b05 b04 b03 b02 b01 b00 | b15 b14 b13 b12 b11 b10 b09 b08 | b23 b22 b21 b20 b19 b18 b17 b16 | ... Nix32表現では、以下のように5bitずつ取り出していく。 1 2 3 4 5 b04 b03 b02 b01 b00 b09 b08 | b07 b06 b05 b14 b13 b12 b11 b10 b19 b18 b17 b16 | b15 ... その5bitに文字を対応させる。具体的には、5bit値idxに対して、以下の文字列のchars[idx]を対応させる。 ...

2024-04-21 · (updated 2024-04-21) · 3 min · 622 words

Nixのいくつかの処理をPythonで実装してみる

Nixのパッケージ・derivationの探り方まとめにて色々なコマンドを紹介したが、それらがNix内部でどう処理されているのかを知りたくなり、その過程でPython実装を書いた。 目的は、以下の2つの処理をPythonで実装することである。 build dependenciesを(間接的なものも含め)出力する runtime dependenciesを(間接的なものも含め)出力する なお、公式ではどちらもnix-store --query --requisitesないしnix-store --query --treeで出力可能である。 (parse) drvファイルを読み込み、パースする この先の処理を実装するにあたって、drvから情報を取り出す必要があるので、ここでパーサーを実装する。 まずdrvのファイル形式は以下のようなものであった。見やすいように改行を挟んでいるが、実際には無い。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 bombrary@nixos:~$ nix derivation show `which ls` | jq -r 'to_entries[].key' | xargs cat Derive( [("debug","/nix/store/b073nwng2fy24zaqbdx6zbimxkad7dyk-coreutils-full-9.3-debug","",""), ("info","/nix/store/1pd076gkjwh0wdv8cnxy6p7kl141jnk2-coreutils-full-9.3-info","",""), ("out","/nix/store/03167shkax5dxclnv6r3sd8waa6lq7ny-coreutils-full-9.3","","")], [("/nix/store/5q67fxm276bdp87jpmckvz3n81akw6a5-perl-5.38.2.drv",["out"]), ("/nix/store/98sv0g544bqmks49d6vgylbkh9sccdvm-attr-2.5.1.drv",["dev"]), ("/nix/store/9jcfzyyb0h86mvc31s9qmxs6lncqrwhc-acl-2.3.1.drv",["dev"]), ("/nix/store/akpwym6q116hivciyq2vqj9n5jk9f5i6-xz-5.4.4.drv",["bin"]), ("/nix/store/d1qldhg6iix84bqncbzml2a1nw8p95bg-gmp-with-cxx-6.3.0.drv",["dev"]), ("/nix/store/ks5ivc59k57kwii93qlsfgcx2a7xma1k-autoreconf-hook.drv",["out"]), ("/nix/store/mnrjvk62d35v8514kc5w31fg3py0smr8-coreutils-9.3.tar.xz.drv",["out"]), ("/nix/store/mvvhw7jrrr8wnjihpalw4s3y3g7jihgw-stdenv-linux.drv",["out"]), ("/nix/store/szciaprmwb7kdj7zv1b56midf7jfkjnw-bash-5.2-p15.drv",["out"]), ("/nix/store/wd100hlzyh5w9zkfljkaagp87b7h7733-openssl-3.0.12.drv",["dev"])], ["/nix/store/1cwqp9msvi5z8517czfl88dd42yhrdwg-separate-debug-info.sh", "/nix/store/v6x3cs394jgqfbi0a42pam708flxaphh-default-builder.sh"], "x86_64-linux", "/nix/store/7dpxg7ki7g8ynkdwcqf493p2x8divb4i-bash-5.2-p15/bin/bash", ["-e","/nix/store/v6x3cs394jgqfbi0a42pam708flxaphh-default-builder.sh"], [("FORCE_UNSAFE_CONFIGURE",""), ("NIX_CFLAGS_COMPILE",""), ("NIX_LDFLAGS",""), ("__structuredAttrs",""), ("buildInputs","/nix/store/hwb08pf2byl2a1rnmaxq56f389h6b6yn-acl-2.3.1-dev /nix/store/djciacxl96yr2wd02lcxyn8z046fzrqr-attr-2.5.1-dev /nix/store/1fszsmhmlhbi4yzl2wgi08cfw0dng7pq-gmp-with-cxx-6.3.0-dev /nix/store/2d8yhfx7f2crn8scyzdk6dg3lw7y1ifh-openssl-3.0.12-dev"), ("builder","/nix/store/7dpxg7ki7g8ynkdwcqf493p2x8divb4i-bash-5.2-p15/bin/bash"), ("cmakeFlags",""), ("configureFlags","--with-packager=https://nixos.org --enable-single-binary=symlinks --with-openssl gl_cv_have_proc_uptime=yes"), ("debug","/nix/store/b073nwng2fy24zaqbdx6zbimxkad7dyk-coreutils-full-9.3-debug"), ("depsBuildBuild",""), ("depsBuildBuildPropagated",""), ("depsBuildTarget",""), ("depsBuildTargetPropagated",""), ("depsHostHost",""), ("depsHostHostPropagated",""), ("depsTargetTarget",""), ("depsTargetTargetPropagated",""), ("doCheck","1"), ("doInstallCheck",""), ("enableParallelBuilding","1"), ("enableParallelChecking","1"), ("enableParallelInstalling","1"), ("info","/nix/store/1pd076gkjwh0wdv8cnxy6p7kl141jnk2-coreutils-full-9.3-info"), ("mesonFlags",""), ("name","coreutils-full-9.3"), ("nativeBuildInputs","/nix/store/nsl35d8x8jp0vy8n4xy8sx9v68gdh444-autoreconf-hook /nix/store/rza0ib08brnkwx75n7rncyjq97j76ris-perl-5.38.2 /nix/store/3q6fnwcm677l1q60vkhcf9m1gxhv83jm-xz-5.4.4-bin /nix/store/1cwqp9msvi5z8517czfl88dd42yhrdwg-separate-debug-info.sh"), ("out","/nix/store/03167shkax5dxclnv6r3sd8waa6lq7ny-coreutils-full-9.3"), ("outputs","out info debug"), ("patches",""), ("pname","coreutils-full"), ("postInstall",""), ("postPatch","# The test tends to fail on btrfs, f2fs and maybe other unusual filesystems.\nsed '2i echo Skipping dd sparse test && exit 77' -i ./tests/dd/sparse.sh\nsed '2i echo Skipping du threshold test && exit 77' -i ./tests/du/threshold.sh\nsed '2i echo Skipping cp reflink-auto test && exit 77' -i ./tests/cp/reflink-auto.sh\nsed '2i echo Skipping cp sparse test && exit 77' -i ./tests/cp/sparse.sh\nsed '2i echo Skipping rm deep-2 test && exit 77' -i ./tests/rm/deep-2.sh\nsed '2i echo Skipping du long-from-unreadable test && exit 77' -i ./tests/du/long-from-unreadable.sh\n\n# Some target platforms, especially when building inside a container have\n# issues with the inotify test.\nsed '2i echo Skipping tail inotify dir recreate test && exit 77' -i ./tests/tail-2/inotify-dir-recreate.sh\n\n# sandbox does not allow setgid\nsed '2i echo Skipping chmod setgid test && exit 77' -i ./tests/chmod/setgid.sh\nsubstituteInPlace ./tests/install/install-C.sh \\\n --replace 'mode3=2755' 'mode3=1755'\n\n# Fails on systems with a rootfs. Looks like a bug in the test, see\n# https://lists.gnu.org/archive/html/bug-coreutils/2019-12/msg00000.html\nsed '2i print \"Skipping df skip-rootfs test\"; exit 77' -i ./tests/df/skip-rootfs.sh\n\n# these tests fail in the unprivileged nix sandbox (without nix-daemon) as we break posix assumptions\nfor f in ./tests/chgrp/{basic.sh,recurse.sh,default-no-deref.sh,no-x.sh,posix-H.sh}; do\n sed '2i echo Skipping chgrp && exit 77' -i \"$f\"\ndone\nfor f in gnulib-tests/{test-chown.c,test-fchownat.c,test-lchown.c}; do\n echo \"int main() { return 77; }\" > \"$f\"\ndone\n\n# intermittent failures on builders, unknown reason\nsed '2i echo Skipping du basic test && exit 77' -i ./tests/du/basic.sh\n"), ("preInstall",""), ("propagatedBuildInputs",""), ("propagatedNativeBuildInputs",""), ("separateDebugInfo","1"), ("src","/nix/store/8f1x5yr083sjbdkv33gxwiybywf560nz-coreutils-9.3.tar.xz"), ("stdenv","/nix/store/kv5wkk7xgc8paw9azshzlmxraffqcg0i-stdenv-linux"), ("strictDeps",""), ("system","x86_64-linux"), ("version","9.3")] ) drvファイルをにらむと、データ型としては以下のパターンしかなさそうだとわかる。 ...

2024-04-05 · (updated 2024-04-16) · 12 min · 2475 words

Nixの外でビルドされた実行バイナリをNixOSで動かす

更新内容 2024/09/14 記事の内容を整理した。もともとRyeを使う際に詰まった記録を記事にしたものだったが、あまりにRye依存になる記述が多かったので、python-build-standaloneを例とした内容に置き換えた。 Ryeのときの話はRyeをNixOS上で動かそうとしたときの記録(2024年2月)に移動した。 前置き NixOSはNixOSの内側で生活するには十分快適だが、その外で作成されたソフトウェアを持ってこようとすると、途端にめんどくさくなる。その例として 動的リンカのパスが解決できず実行バイナリが動かない shebangのパスが解決できずシェルスクリプトが実行できない が挙げられるが、今回は前者の話をする。その解決方法としてpatchelfとnix-ldがあるのでそれを紹介する(後者はenvfsで解決可能だが、もしかしたら後日記事にまとめるかも) この件についてはすでにZennでまとめてくださっている人がいる(参考:NixOS に関する小ネタ集)し、なんならnix-ldの製作者のブログでほぼ同じ内容の記事を書かれていた。が、今一度自分も整理のため、具体的にぶち当たった事例も含めて書いておこうと思う。 要約 nix-ldを導入すれば、NixOS外の実行バイナリが動くようになる 共有ライブラリが足りないなどのエラーが出た場合は、LD_LIBRARY_PATHを指定する nix developで上記環境変数が設定されるようにnixファイルを書いたほうが良い python-build-standaloneがNixOS上で動かせないことの確認 ここでは、外部でビルドされたpythonであるpython-build-standaloneをNixOS上で動かそうとしてみよう。なおpythonはnixpkgsから入手可能であり、通常利用の場合はわざわざ外部からビルド済みのpythonを持ってくる必要はないのだが、今回は例のためにこれを実行することを考える。 pythonのstandaloneをDLしてくる。 1 2 3 4 5 6 7 8 9 10 11 [bombrary@nixos:~]$ curl -LO https://github.com/indygreg/python-build-standalone/releases/download/20240107/cpython-3.12.1+20240107-x86_64-unknown-linux-gnu-install_only.tar.gz % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0 100 64.6M 100 64.6M 0 0 17.0M 0 0:00:03 0:00:03 --:--:-- 24.5M [bombrary@nixos:~]$ tar -xzf cpython-3.12.1+20240107-x86_64-unknown-linux-gnu-install_only.tar.gz [bombrary@nixos:~]$ cd python/bin [bombrary@nixos:~/python/bin]$ ls 2to3 2to3-3.12 idle3 idle3.12 pip pip3 pip3.12 pydoc3 pydoc3.12 python3 python3-config python3.12 python3.12-config 実行してみると、required file not found という、先ほどのNo such file or directoryと同じようなエラーが出てくる。 ...

2024-02-18 · (updated 2024-09-14) · 6 min · 1276 words

Socket通信勉強(2) - Pythonでの書き方/HTTPサーバーもどき作成

PythonでのSocket通信 やってることはCでやったときと同じである。サーバーとクライアントの通信手順は同じだし、関数名も同じである。しかしCで書いた場合に比べてシンプルに書ける。エラーは例外として投げられるため、自分で書く必要がない。またsockaddr_inなどの構造体が登場することはなく、Pythonでのbind関数とconnect関数の引数に直接アドレス・ポートを指定する。 server.py 前回と同じく、以下の手順で通信を行う。 listen(待ち受け)用のソケット作成 - socket 「どこからの接続を待つのか」「どのポートにて待ち受けするのか」を決める - bind関数の引数 ソケットにその情報を紐つける - bind 実際に待ち受けする - listen 接続要求が来たら受け入れる - accept 4によって通信用のソケットが得られるので、それを用いてデータのやりとりをする- send/recv 1 2 3 4 5 6 7 8 9 10 import socket s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.bind(("", 8000)) s.listen(5) (sock, addr) = s.accept() print("Connected by" + str(addr)) sock.send("Hello, World".encode('utf-8')) sock.close() s.close() 上のコードを見れば各関数がどんな形で引数をとって、どんな値を返すのかがわかると思う。いくつか補足しておく。 bind (受け入れアドレス, ポート)というタプルを引数にとる。受け入れアドレスを空文字列にしておけば、どんなアドレスからの接続も受け入れる。つまりCでやったINADDR_ANYと同じ。 1 s.bind(("", 8000)) encode Pythonのstring型をそのまま送ることはできないので、byte型に変換する。これはstring.encodeで行える。 1 sock.send("Hello, World".encode('utf-8')) client.py サーバーとの通信用のソケット作成 - socket サーバが待ち受けている宛先を設定 - connectの引数 2で設定した宛先に対して接続する - connect 1で作ったソケットを用いてデータのやりとりをする。 - send/recv 1 2 3 4 5 6 7 import socket sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect(("localhost", 8000)) data = sock.recv(64) print(data) sock.close() これも2点補足する。 ...

2019-12-08 · (updated 2021-03-03) · 3 min · 536 words

Djangoの勉強でTodoリストを作る

更新(2021/06/13): いくつか間違っていたところがあったので修正。 どんなTodoリストを作るか Todoの登録 情報は短いテキストだけ Todoをリスト表示 Todoをクリックすると削除 サイトの作成 適当なディレクトリで次のコマンドを実行すると、mysiteというディレクトリが作られる。 1 $ django-admin startproject mysite 以降はmysiteディレクトリで作業する。 アプリの作成 mysiteディレクトリにて以下のコマンドを実行すると、todo_listというディレクトリが作られる。ここに実際のアプリの処理を記述していく。 1 $ python3 manage.py startapp todo_list 続いてmysite/mysite/settings.pyを開いて、INSTALL_APPSを以下の記述にする。'todo_list.apps.TodoListConfig'を追加しただけ。これはデータベース作成やテンプレート作成のために、djangoがtodo_listのディレクトリを教えているっぽい。Todo_listConfigかと思ったが違うらしい(エラーで「TodoListConfigだよ」と教えてくれた。優しい)。 1 2 3 4 5 6 7 8 9 INSTALLED_APPS = [ 'todo_list.apps.TodoListConfig', 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', ] viewの作成 mysite/todo_list/views.pyを編集する。とりあえずviewが動くかどうかだけ確認したいので、レスポンスは適当な文字列にする。 1 2 3 4 5 6 from django.http import HttpResponse # Create your views here. def index(request): return HttpResponse('Hello') urlの設定 まずmysite/mysite/urls.pyの設定をする。urls.pyとは「どんなurlにアクセスされたらどんなviewに処理を任せるか」を記述したものっぽい。ここでは、todo_list/で始まるurlだったらtodo_list/urls.pyに処理を任せるように書いている。 1 2 3 4 5 6 7 from django.contrib import admin from django.urls import include, path urlpatterns = [ path('todo_list/', include('todo_list.urls')), path('admin/', admin.site.urls), ] ということでmysite/todo_list/urls.pyの設定をする。恐らく存在しないので新しく作成する。todo_list/以降に何も指定されなかったら表示をviews.pyのindex関数に任せるように書いている。 ...

2019-11-15 · (updated 2021-06-13) · 4 min · 776 words