Rustプログラムでpolarsの空DataFrameを作る

ぶっちゃけ、公式ドキュメントですぐにわからなかったのでメモ。

やりたかったこと

ArcやRwLockを使って、DataFrameにデータの追加と読み込みを別スレッドにしたく、 DataFrameの初期化で空のDataFrameが欲しかった。

結論

スキーマを使わない場合は、default()を使用する。

use polars_core::prelude::*;

fn main() {
    let df = DataFrame::default();
    println!("{:?}", df);
    assert!(df.is_empty());
}

でも今回のような使用用途だと、スキーマが必要。 スキーマを定義したい場合はこちら

use polars_core::prelude::*;

fn main() {
    let sc: Schema = Schema::from_iter(vec![
        Field::new("Thing", DataType::String), 
        Field::new("Diameter (m)", DataType::Float64)
        ]);
    let df = DataFrame::from(&sc);
    println!("{:?}", df);
    assert!(df.is_empty());
}

あと書き

できればSchemaはstructとかから生成できるといいのだが調査中

ターミナルプロンプトを自作するには

ターミナルのプロンプトを自分仕様にカスタマイズしたいと思ったので調べました。

ターミナルのプロンプトにはPS1 ~ 4があり、コマンドの入力待ち状態に表示れされているのものはPS1に格納されたものを出力しています。

PS1~4の内容

PS1: プライマリプロンプト

PS1は、プライマリプロンプトとして知られています。これは、通常のコマンドを入力する際に表示されるプロンプトです。自分のスタイルに合わせてカスタマイズすることで、ターミナルがより使いやすくなる。

例えば、PS1をカスタマイズしてユーザー名やホスト名、現在のディレクトリを表示するようにすることができる。また、異常終了したプロセスや最後のコマンドの実行結果なども表示させることが可能。

PS2: セカンダリプロンプト

PS2は、マルチラインのコマンドを入力する際に表示されるセカンダリプロンプト。通常は">"だが、これもユーザーが自由に変更することができる。

PS3: プロンプト用のメッセージ

PS3は、select時等のコマンド内でユーザからの入力を受け付ける際に表示されるもの。基本はコマンドに依存するのでわざわざ変えることはないのかなという感じ

PS4: プロンプトの色とスタイル

PS4は、デバッグモードで使用される。

PS1の更新タイミング

PS1はさまざまなタイミングで更新が走っており、インタラクティブに内容を変えます。

PS1の更新タイミングは、主に次のいくつかの場面がある。

  • 起動時: PS1は、シェルが起動したときに設定されます。ユーザーが新しいセッションを開始すると、シェルの初期化プロセスでPS1が適用されます。

  • コマンド実行後: PS1を変更すると、その変更は即座に反映されます。ただし、新しいプロンプトが表示されるのは、次にコマンドを入力する際です。コマンドを入力すると、新しいPS1が適用されて新しいプロンプトが表示されます。

  • 設定ファイルの読み込み時: PS1の設定は、通常は.bashrcや.bash_profileなどのシェルの設定ファイルに書かれることがあります。これらのファイルが読み込まれると、PS1の設定も更新されます。ログイン時にこれらの設定ファイルが読み込まれ、新しいプロンプトが表示されます。

  • プロンプトの変更コマンド: ユーザーが手動でPS1を変更するコマンドを実行した場合、その時点で新しいプロンプトが反映されます。

要するに、PS1は基本的にシェルのセッションが始まる際やコマンドが実行されるたびに適用され、プロンプトが変更されます。

プロンプトエスケープ

プロンプトエスケープは、ターミナルのプロンプトに表示されるテキストを装飾するための特殊な制御文字やシーケンスです。これを使用することで、例えば文字の色やスタイル、背景色などを変更して目立たせることができる。これは、特にシェルプロンプトをカスタマイズする際に便利

装飾のほか、日付や今いるディレクトリなどの取得もこれで行える

zsh.sourceforge.io

非同期でより高速に

下のリンクにあるように非同期でやるとちょっと込み入ったプロンプトも高速にできる

www.m3tech.blog

ちょっと自己流プロンプトを作って見たいと思います。

【Rust】HTTPのレスポンスをparseする

よくやるのでメモ

関連パッケージ

[dependencies]
reqwest = { version = "0.11.22", features = ["json"] }
serde = { version = "1.0.188", features = ["derive"] }
tokio = { version = "1.32.0", features = ["full"] }

サンプルコード

use serde::Deserialize;

#[derive(Debug, Deserialize)]
struct User {
    id: i32,
    name: String
}

#[tokio::main]
async fn main() {
    let response: User = reqwest::get("http://localhost:3000/user").await.unwrap().json::<User>().await.unwrap();
    println!("{:?}", response);
}

ちなみに

httpのクレートにhyperreqwestがよく使われるが、現在はreqwestを使うのが推奨らしい

低レイヤであれこれやりたい場合のみhyperを使いましょうということらしい hyperの作者はreqwestの作者でもあり、その本人がreqwestを推奨しているとどこかで読んだ。

AsRef<T>に対する解釈

AsRefトレイトがよくわかない。。。

よくわかならいなりに自分が解釈したものを書く

結論

AsRef<T>トレイトを実装してるものは.as_ref()することで&Tと見做して処理することができるよ

ということをコンパイラに教えるアノテーション的な意味が強いというふうに解釈した。

なぜas_ref()の戻り値が&T(参照)である必要があるのか

自分の解釈: 複数種類の型に対して共通の処理をさせるため。

一度参照にしてあげることで、元来の型を隠すことができる。

あえて型を隠し、ジェネリクスTで自分の好きな型に見せかけることで複数の型に対して共通の処理を行わせること(= コンパイルを通すこと)ができる。

ファイル操作ではトレイト境界にAsRef<Path>を用いる。

ファイルパスの型はstr, String, OsStrなどの複数の型を利用できるようになっている。

AsRef<Path>を使うことで内部ではstr, String, OsStrという型を一度隠し、Pathというラベルに張り替えてコンパイラを騙して処理させることができる。

という具合だ。

GRCを用いたsubmoduleの追加

AWSで開発するにあたってCodeCommitを使っています。

GRCはAWSのクレデンシャルを使ってgitが扱えるので非常に便利ですが、git submoduleで少々つまづいたのでメモ

何もしないとgit submoduleはプロトコルcodecommitを使えません。

GRCを用いたsubmoduleの追加

以下のコマンドでcodecommitプロトコルを常に許可する。

git config --global --add protocol.codecommit.allow always

そうするとgit submodule addすることが可能。この時credentialをurlに埋め込むとmoduleのconfigに残る。

git submodule addしたらルートに.gitmodulesを追加し以下を記述

[submodule "MODULE_NAME"]
    path = DIR_NAME
    url = GCR_URL

submoduleの同期

cloneして来たらsubmoduleは空の状態なので同期するためにgit submodule update --initしたいところだが、実行するとcredentialで怒られる

全員が共通のcredential名にして.gitmodulesに記載するというルールはナンセンスなのと、デプロイ時にもcredentialが必要になってしまうのでNG

代わりにプライベートURLを設定してそのURLにcredentialを埋め込んであげる。

git config submodule.terraform_modules.url PRIVATE_URL

【Python】プリミティブな型をちょっとリッチに拡張する

きっかけ

今の案件で基本的にはintの振る舞いで構わないんだけど、 その数値の持つ意味をコードに反映したいということになった。

たとえば、型で具体的に何の値を指しているのかをアノテーションをしたり、ちょっとした変換処理やバリデーションや制約を数値型に実装したり、メソッドを生やしたり。

基本的にはintの振る舞いで構わないというのがミソ

なのでがっつりクラスを作るようなことはしたくなかった。

組み込みライブラリを使う

typingライブラリのNewTypeはintやstrといったプリミティブな型に名前をつけることができる。

from typing import NewType

UnixTime = NewType("UnixTime", int) 

これで、UnixTimeという名前でintと全く同じ振る舞いをする型を生成できる。

型で具体的に何の値を指しているのかをアノテーションするのに役立つ。

バリデーションや制約をつける

上記のNewTypeはバリデーションとかができない。

じゃあどうするか

Pythonのintやstrは中身はclassである。

なので継承して、新たな型を作りだすことが可能

たとえば、

from datetime import datetime

class UnixTime(int):

    def __new__(cls, value):
        # vlaueをゴニョゴニョする処理
        ...
        
        return super().__new__(cls, value)
    
    def to_datetime(self) -> datetime:
        return datetime.fromtimestamp(self)

これでinstance化の時にバリデーションしたり、その付加した意味に適したメソッドを生やすことができる。

__new__を使っているのは、intやstrなどの不変な型(≒プリミティブっぽいやつ)を作成するには__init__は遅すぎるため。

__new__はオブジェクトを生成する前に実行される。

【Python】globalで環境変数を呼ぶことの罪

環境変数をglobalで読むコードを書く人がいるが、私はそれに関して完全にアンチである

※念の為書くが、決して環境変数自体を否定したいわけではない

環境変数をglobalで読むことで生まれる被害

  • importしただけで評価されるので使ってなくても要求される
  • 忘れがちで、2度手間を生む
  • テストが冗長になる(環境変数にあることがテストできるわけではないのに環境変数を読むためのコードが必要になる)
  • 書き換えると他のテストに影響を出す可能性がある

どれも小手先(環境でif分岐したり、環境変数を戻したり)でどうにかできる被害かもしれない。

でも塵が積もると山になって、単体テストのモチベーションを下げ、テストをやらなくなるのだ

じゃあどうするの?

ケースによるが僕ならたとえばこうする

class Hoge:
    def __init__(self, a, b):
        ...

    @staticmethod
    def from_env() -> Hoge:
        _a = os.environ[...]
        _b = os.environ[...]
        return Hoge(_a, _b)

from_envのようなメソッドを作成してその中で読む

こうすることで、単体テストの範囲では、環境変数が不要になる。

from_envをmockにしてしまうことも可能である

環境変数に入っているかという観点のテストは?

そもそも、その観点は単体テストでは確認不可能であるQED

p.s. クラス変数の初期化で代入するのも同じ罪です。いつその環境変数を読むコードが評価されるのかを考えましょう