みおもん倶楽部 技術雑記 (original) (raw)

いつも恒例のn番煎じシリーズいきます。

やること

pythonスクリプト(docstring付き)をhtmlファイルのドキュメントに変換します。 docstring自体の記法はここでは触れません。

事前準備

pipでパッケージを入れておきます。

pip install sphinx

あとで使うので、テーマファイルも入れます。

pip install sphinx_rtd_theme

ソースコードを用意

. └── src ├── entry.py ├── mod1.py └── mod2.py

上記のような感じでpythonファイルをいくつか用意します。とりあえずdocstringがちょっと書かれていれば中身はなんでもいいです。 ここでは以下のような感じでざっくり書いてみました。

entry.py

from mod1 import split_basename, compute_awesome_value, check_list_has_even_length from mod2 import DownCounter

def main(): """ エントリーポイント

以下の関数を順に実行する

- split_basename
- compute_awesome_value
- check_list_has_even_length
- DownCounterのインスタンス作成
- DownCounterのtickメソッドを5回実行
"""
rev1 = split_basename("hello/world.txt")
rev2 = compute_awesome_value(2, 3.4, True)
rev3 = check_list_has_even_length([1, 2, 3])

print(rev1)
print(rev2)
print(rev3)

counter = DownCounter(5, 1)
for _ in range(5):
    counter.tick()

if name == "main": main()

mod1.py

def split_basename(filepath: str) -> str: """ファイルパスからベース名を取得する

Args:
    filepath (str): ベース名を取得するファイルパス

Returns:
    str: ファイルパスのベース名
"""
return filepath.split('/')[-1]

def compute_awesome_value(i: int, f: float, multiple: bool) -> float: """条件に基づいて素晴らしい値を計算する

Args:
    i (int): 整数値
    f (float): 浮動小数点数
    multiple (bool): 掛け算または足し算を行うかを示すブール値

Returns:
    float: 算術演算の結果
"""
if multiple:
    return i * f
else:
    return i + f

def check_list_has_even_length(numlist: list[int]) -> bool: """リストの長さが偶数かどうかを確認する

Args:
    numlist (list[int]): 整数のリスト

Returns:
    bool: リストの長さが偶数の場合はTrue、そうでない場合はFalse
"""
if type(numlist) is not list:
    return False

return len(numlist) % 2 == 0

mod2.py

class DownCounter: """ カウンタ機能を持ったクラス

Attributes:
    count: カウンタの値

Methods:

    - increment: カウンタを1増やす
    - decrement: カウンタを1減らす
    - set: カウンタの値を設定する
    - get: カウンタの値を取得する
"""

initial_count: int
count: int
step: int

def __init__(self, count: int = 100, step: int = 1):
    """
    カウンタ値とstepを初期化する

    Args:
        count: カウンタの初期値、デフォルトは100
        step: カウンタを増減させる値、デフォルトは1

    """
    if type(count) is not int:
        raise ValueError("count must be an integer")

    if type(step) is not int:
        raise ValueError("step must be an integer")

    if count < 0:
        raise ValueError("count must be a non-negative integer")

    if step < 0:
        raise ValueError("step must be a non-negative integer")

    self.initial_count = count
    self.count = count
    self.step = step

def tick(self):
    """
    カウンタ値を1減らし、0未満になった場合は警告を出力する
    """
    self.count -= self.step

    if self.count <= 0:
        print("beep beep beep!!")

def reset(self):
    """
    カウンタ値を初期値にリセットする
    """
    self.count = self.initial_count

ドキュメントの雛形生成

sphinx-apidocで作っていきます。

sphinx-apidoc ./src -a -F -o docs

いろいろファイルが生成されますが、ビルドに関わる設定はconf.pyに対して行います。

説明

-a についてはこんな感じです。

import os import sys sys.path.insert(0, '../src')

他にもオプションがありますが、詳しく以下からご確認ください。

https://www.sphinx-doc.org/ja/master/man/sphinx-apidoc.html

ファイル構成は以下のようになります。

. ├── docs │   ├── Makefile │   ├── _build │   ├── _static │   ├── _templates │   ├── conf.py │   ├── entry.rst │   ├── index.rst │   ├── make.bat │   ├── mod1.rst │   └── mod2.rst └── src ├── entry.py ├── mod1.py └── mod2.py

ドキュメントのビルド

docsに移動してビルドしていきます。

Makefileの確認

以下のような内容になっています。

Minimal makefile for Sphinx documentation

You can set these variables from the command line, and also

from the environment for the first two.

SPHINXOPTS ?= SPHINXBUILD ?= sphinx-build SOURCEDIR = . BUILDDIR = _build

Put it first so that "make" without argument is like "make help".

help: @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" (SPHINXOPTS)(SPHINXOPTS) (SPHINXOPTS)(O)

.PHONY: help Makefile

Catch-all target: route all unknown targets to Sphinx using the new

"make mode" option. (O)ismeantasashortcutfor(O) is meant as a shortcut for (O)ismeantasashortcutfor(SPHINXOPTS).

%: Makefile @$(SPHINXBUILD) -M @"@ "@"(SOURCEDIR)" "$(BUILDDIR)" (SPHINXOPTS)(SPHINXOPTS) (SPHINXOPTS)(O)

sphinx-buildのラッパとして動作するようです。

単にmakeするとhelpが表示されるようなので、試してみます。

Sphinx v7.2.6 Please use `make target' where target is one of html to make standalone HTML files dirhtml to make HTML files named index.html in directories singlehtml to make a single large HTML file pickle to make pickle files json to make JSON files htmlhelp to make HTML files and an HTML help project qthelp to make HTML files and a qthelp project devhelp to make HTML files and a Devhelp project epub to make an epub latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter latexpdf to make LaTeX and PDF files (default pdflatex) latexpdfja to make LaTeX files and run them through platex/dvipdfmx text to make text files man to make manual pages texinfo to make Texinfo files info to make Texinfo files and run them through makeinfo gettext to make PO message catalogs changes to make an overview of all changed/added/deprecated items xml to make Docutils-native XML files pseudoxml to make pseudoxml-XML files for display purposes linkcheck to check all external links for integrity doctest to run all doctests embedded in the documentation (if enabled) coverage to run coverage check of the documentation (if enabled) clean to remove everything in the build directory

いざビルド

htmlターゲットがあるので、それを実行してみましょう。

make html

ビルドできたようです。

. ├── docs │   ├── Makefile │   ├── _build │   │   ├── doctrees │   │   │   ├── entry.doctree │   │   │   ├── environment.pickle │   │   │   ├── index.doctree │   │   │   ├── mod1.doctree │   │   │   └── mod2.doctree │   │   └── html │   │   ├── _modules │   │   │   ├── entry.html │   │   │   ├── index.html │   │   │   └── mod1.html │   │   ├── _sources │   │   │   ├── entry.rst.txt │   │   │   ├── index.rst.txt │   │   │   ├── mod1.rst.txt │   │   │   └── mod2.rst.txt │   │   ├── _static │   │   │   ├── alabaster.css │   │   │   ├── basic.css │   │   │   ├── custom.css │   │   │   ├── doctools.js │   │   │   ├── documentation_options.js │   │   │   ├── file.png │   │   │   ├── language_data.js │   │   │   ├── minus.png │   │   │   ├── plus.png │   │   │   ├── pygments.css │   │   │   ├── searchtools.js │   │   │   └── sphinx_highlight.js │   │   ├── entry.html │   │   ├── genindex.html │   │   ├── index.html │   │   ├── mod1.html │   │   ├── mod2.html │   │   ├── objects.inv │   │   ├── py-modindex.html │   │   ├── search.html │   │   └── searchindex.js │   ├── _static │   ├── _templates │   ├── conf.py │   ├── entry.rst │   ├── index.rst │   ├── make.bat │   ├── mod1.rst │   └── mod2.rst └── src ├── pycache │   ├── entry.cpython-312.pyc │   ├── mod1.cpython-312.pyc │   └── mod2.cpython-312.pyc ├── entry.py ├── mod1.py └── mod2.py

ビルド結果

alabaster entry

alabaster entry

alabaster mod1

alabaster mod1

alabaster mod2

alabaster mod2

とりあえず情報は出揃っているようです。

テーマを変える

少しテーマが味気ないように感じるので、よく見る感じのテーマに変えてみます。

conf.pyを少しいじります。

Makefileのあるディレクトリに移動してもう一度ビルドします。

make html

rtd entry

rtd entry

rtd mod1

rtd mod1

rtd mod2

rtd mod2

いい感じです。

ソースを更新したら

./src/配下のpythonスクリプトを編集した場合は、もう一度 make htmlを実行すればhtmlファイルに変更が反映されます。

dockerで立てたコンテナにsshでアクセスして開発を行いたいなと思いました。

N番煎じもいいところですが、ちょっとハマったので記録を残しておきます。

目指すもの

ファイル構成

. ├── Dockerfile ├── docker-compose.yaml └── setup.sh

Dockerfile

FROM ubuntu:latest

ビルド時、new_userという引数を受け取る

ARG new_user

ユーザを作成

RUN useradd -ms /bin/bash ${new_user}

sshディレクトリを作成

RUN mkdir /home/${new_user}/.ssh

公開鍵をコピー(terminal.pubが存在している必要がある)

COPY terminal.pub /home/${new_user}/.ssh/authorized_keys

sshサーバとsudoコマンドをインストール

RUN apt update && apt install -y openssh-server sudo && apt clean

ひとまずパスワードを設定、あとで変えたほうがいい

RUN echo "${new_user}:password" | chpasswd

sudoに追加

RUN gpasswd -a ${new_user} sudo

ssh用のディレクトリを作成

RUN mkdir /var/run/sshd

パスワードを使用したログインを許可しない

RUN sed -i -r 's/^#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config

公開鍵認証を許可

RUN sed -i -r 's/#AuthorizedKeysFile/AuthorizedKeysFile/' /etc/ssh/sshd_config

ポートを22から2222に変更

RUN sed -i -r 's/^#Port 22/Port 2222/' /etc/ssh/sshd_config

RUN /usr/sbin/sshd

EXPOSE 2222

ENTRYPOINT [ "/usr/sbin/sshd", "-D" ]

docker-compose.yaml

version: "3" services: terminal: build: args: - new_user=${USER} context: . dockerfile: Dockerfile container_name: terminal privileged: true tty: true ports: - "2222:2222"

new_user=${USER}の部分では、ホスト側の環境変数を使用しています。 別の名前を使用したい場合はここを変更します。

ボリュームをマウントしたい場合、dnsを設定したい場合、他のコンテナを付け足したい場合、適宜改造してください。

setup.sh

手元ではコンテナが起動していたら止めるとか、キーペアが存在してたら中止するとかを付け加えていますが、その部分は省略して、エッセンスだけ抜粋します。

ssh-keygen -t ed25519 -C "" -f ~/.ssh/terminal -N ""

cp ~/.ssh/terminal.pub .

docker compose up -d --build

ssh localhost -p 2222 -i ~/.ssh/terminal

実行手順

上記記載済みですが、terminal.pubを用意してdocker compose upを実行すればOKです。

xxxxxxxx@yyyyyyyy zzzzzzzz % ./setup.sh
[+] Building 0.0s (15/15) FINISHED docker:desktop-linux => [terminal internal] load build definition from Dockerfile 0.0s => => transferring dockerfile: 880B 0.0s => [terminal internal] load metadata for docker.io/library/ubuntu:latest 0.0s => [terminal internal] load .dockerignore 0.0s => => transferring context: 2B 0.0s => [terminal 1/10] FROM docker.io/library/ubuntu:latest 0.0s => [terminal internal] load build context 0.0s => => transferring context: 595B 0.0s => CACHED [terminal 2/10] RUN useradd -ms /bin/bash xxxxxxxx 0.0s => CACHED [terminal 3/10] RUN mkdir /home/xxxxxxxx/.ssh 0.0s => CACHED [terminal 4/10] COPY terminal.pub /home/xxxxxxxx/.ssh/authorized_keys 0.0s => CACHED [terminal 5/10] RUN apt update && apt install -y openssh-server && apt clean 0.0s => CACHED [terminal 6/10] RUN mkdir /var/run/sshd 0.0s => CACHED [terminal 7/10] RUN sed -i -r 's/^#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config 0.0s => CACHED [terminal 8/10] RUN sed -i -r 's/#AuthorizedKeysFile/AuthorizedKeysFile/' /etc/ssh/sshd_config 0.0s => CACHED [terminal 9/10] RUN sed -i -r 's/^#Port 22/Port 2222/' /etc/ssh/sshd_config 0.0s => CACHED [terminal 10/10] RUN /usr/sbin/sshd 0.0s => [terminal] exporting to image 0.0s => => exporting layers 0.0s => => writing image sha256:fbe41ddbf46c8601b2709640c58c66f311c66628c5cc53b0429877270bf0bad2 0.0s => => naming to docker.io/library/zzzzzzzz-terminal 0.0s [+] Running 1/2 ⠙ Network zzzzzzzz_default Created 0.1s ✔ Container terminal Started 0.1s

xxxxxxxx@yyyyyyyy zzzzzzzz % ssh localhost -p 2222 -i ~/.ssh/terminal Welcome to Ubuntu 22.04.1 LTS (GNU/Linux 6.6.16-linuxkit aarch64)

This system has been minimized by removing packages and content that are not required on a system that users do not log into.

To restore this content, you can run the 'unminimize' command.

The programs included with the Ubuntu system are free software; the exact distribution terms for each program are described in the individual files in /usr/share/doc/*/copyright.

Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by applicable law.

xxxxxxxx@c264796d36c0:~$

以下の情報は置き換えています。

Dockerfileとかdocker-compose.yamlは雰囲気で書いていますので、変な部分あるかもしれません。

serdeはserializeとdeserializeを行うクレートです。

関数を呼んで機能を実現する「わかりやすいライブラリ」ではなく、インターフェースというか、プロトコルというか、1段メタな機能を実現するツールであるように思います。attributeで目印をつけ、たとえばserde_jsonという別クレートで実際のシリアライズ・デシリアライズを行うと理解しています。

以下、jsonとstructの相互変換を行う例を雑にまとめてみます。

必要クレート

serde = { version = "1.0.130", features = ["derive"] } serde_json = "1.0.68"

準備

use serde::{Deserialize, Serialize}; use std::fs;

#[derive(Serialize, Deserialize, Debug)] struct Person { name: String, age: u8, }

structからJSON

serde_json::to_writer(&mut file, &p)

JSONからstruct

serde_json::from_reader::<std::fs::File, Person>

もしくは

serde_json::from_str::(&content)

全体

use serde::{Deserialize, Serialize}; use std::fs;

const JSON_FILE_NAME: &str = "person.json";

#[derive(Serialize, Deserialize, Debug)] struct Person { name: String, age: u8, }

fn gen_json() { let json = r#" { "name": "John Doe", "age": 43 } "#;

let p: Person = serde_json::from_str(json).unwrap();

let mut file = std::fs::File::create(JSON_FILE_NAME).unwrap();
serde_json::to_writer(&mut file, &p).unwrap();
println!("{:?}", p);

}

fn read_json_fs() { let file = std::fs::File::open(JSON_FILE_NAME).unwrap();

match serde_json::from_reader::<std::fs::File, Person>(file) {
    Ok(p) => println!("{:?}", p),
    Err(e) => println!("error: {}", e),
}

}

fn read_json_str() { let content = fs::read_to_string(JSON_FILE_NAME).unwrap();

match serde_json::from_str::<Person>(&content) {
    Ok(p) => println!("{:?}", p),
    Err(e) => println!("error: {}", e),
}

}

fn main() {

read_json_fs();
read_json_str();

}

結果のJSON

{"name":"John Doe","age":43}

JSONの値を不正にしてみる

{"name":"John Doe","age":1000}

注:Personのageはu8

fn main() {

read_json_fs();
read_json_str();

}

結果

error: invalid value: integer 1000, expected u8 at line 1 column 30 error: invalid value: integer 1000, expected u8 at line 1 column 29

上記、jsonとstructの例でしたが、例えばHashMapとstructではこうもいかないと思います。 なぜかというと、HashMapはkeyとvalueが静的に決まっている必要があり、nameを取得したらStringだけど、ageを取得したらu8、というような表現力がHashMapにはないからです。(というかそういうことをしたいならstruct)

あるいはHashMapに全部stringで入れて(u8も"20"などのようにStringで入れる)、structと相互変換する、も方法としてはアリかもしれませんが、まあだったらJSONでいいのでは、と思っています。

何を思ってこんなことを書いているかというと、なんらかのデータをデータベースから取得し、一時的にHashMapに入れ、structに変換するのはアリなのか?と思ったのですが、HashMapよりはjsonなどの方が取り回ししやすそうだね、と思った次第です。

無論、データベースから値を取るならそれ用のクレートを使うべきです。(serde_dynamodbなど)

注意:Linux or macユーザを想定しています。windowsユーザも、細部は異なると思いますが、適用可能なはずです。

キーペアの作成

キーペアのディレクトリへ移動します

cd ~/.ssh

キーペアを生成します。ED25519にしますが、RSAとかでもいいと思います。

ssh-keygen -t ed25519

以下のような画面になるはずです。

username@hostname .ssh % ssh-keygen -t ed25519 Generating public/private ed25519 key pair. Enter file in which to save the key (/Users/username/.ssh/id_ed25519): github⭐️1 Enter passphrase (empty for no passphrase):⭐️2 Enter same passphrase again:⭐️2 Your identification has been saved in github Your public key has been saved in github.pub The key fingerprint is: SHA256:xa/hogehogehogehogehogehogehoge/w username@hostname.local

⭐️1 キー名を入力します。github、gitlab、codecommitなど使い分けるならそれがわかるように、もしくはデフォルトのままにして使い回してもOKです。ここではgithubと入力することにします。

⭐️2 パスフレーズを入力しますが私は普段空欄です。現場のルールに従ってください。

キーの登録

以下のURLを開いてください。

https://github.com/settings/keys

以下のような画面になるはずです。

sshkeyの登録画面

ここでNew SSH Keyをクリックします。以下のような画面になるはずです。

キーペアの登録画面

なお、公開鍵は以下のコマンドで取得できます。

cat ~/.ssh/github.pub

キーを登録

github側の作業はこれでOKですが、もうちょっと続きます。

~/.ssh/configの設定

~/.ssh/configファイルを設定します。

編集コマンドはviでもcatでもなんでもいいですが、macではopenコマンドを使えば、多少不慣れな人でも操作可能かと思います。

open ~/.ssh/config

以下の内容を追加します。キー名は適宜置き換えてください。

Host github github.com Hostname github.com IdentityFile ~/.ssh/github User git

接続確認のために、以下を実行します。

ssh -T git@github.com

以下のように表示されれば成功です。

username@hostname .ssh % ssh -T git@github.com Hi Username! You've successfully authenticated, but GitHub does not provide shell access.

追記:sshとは

そもそもsshとはSecure SHellの略で、ネットワーク経由で他のPCにアクセス・操作するための、クライアントとサーバ構成のプロトコルです。ごく簡単に言えば、sshクライアントがLinuxコマンドをネットワーク経由で送り付け、sshサーバがそれを解釈して実行し、その結果をネットワーク経由で送り返す、という挙動になります。(さらにいうと、Linuxコマンドを送るだけがsshではない)

種々のコマンドの実行を許可するので認証が当然必要になるのですが、大雑把にIDパスワードを使う方法とキーペアを使う方法があり、特に後者は公開鍵認証と呼ばれます。キーペアのうち公開して良い方(公開鍵)をsshサーバにあらかじめ登録しておき、認証時にはsshクライアントのみが持っているはずの秘密鍵を使って認証をするというものですが、詳しい挙動は適宜ググってください。

さて本題ですが、githubではローカル環境と接続する際に、httpとsshの2種類の方法で認証をすることが可能です。httpではIDとパスワード、sshではキーペアを用いた認証をするようです。ただ、githubへのアクセスにあたりsshを使ってLinuxコマンドを送りつけるのかというと、少なくともユーザがsshで接続し、Linuxコマンドを送りつけるということはありません。内部で何らかの形で使っていると思われますが、詳細を知らなくてもgithubは使えるので、気にしないことにします。

ViteでSvelteをざっくり動かしてみる

create-viteパッケージをインストール

npm install -g create-vite

svelteプロジェクトを作成する

create-vite my-svelte-app --template svelte

ディレクトリに移動

cd my-svelte-app

依存をインストール

npm install

開発サーバを起動

npm run dev

以下のアドレスとポートでWebサーバプログラムが起動するので、アクセスしてみる。 ポートは環境によって変わるかもしれないので、ターミナルのメッセージを確認。

npm run devの結果

http://localhost:5173/

表示されたアドレスにアクセス。"Count is XXX"をクリックすると値が増える。

コンポーネントを作成

./my-svelte-app/src/lib/Hello.svelte を以下のように作成。

Hello.svelteを追加してみる

5行目と23〜25行目を追加

もう一度以下にアクセスすると、画面が少し変わる

http://localhost:5173/

Click meが増えている

Click meをクリックしてみると

予想通り、alert関数が実行される

今回はdockerを使って、postgresサーバの環境を準備する方法について書いてみます。

dockerの環境が構築済みであることが前提です。 未構築の方はこちらもどうぞ。

Postgresサーバ

dockerを使ってサーバをたてます。

オフィシャルイメージはこちらから確認できます。

起動方法はシンプルで、以下のコマンドでpostgresサーバの起動を行います。

docker run -p 5432:5432 -e POSTGRES_PASSWORD=mysecretpassword -d postgres

ちょっとだけ補足を入れておくと


Postgresクライアント

次に、このサーバに接続して、動作確認をするために、クライアントソフトpsqlをローカル環境にインストールします。 いつものごとく、homebrewを使ってインストールを行いますが、入れるパッケージはlibpqです。libpqの詳細はこちらを参照していただければと思いますが、大事な箇所だけ抜き出すと

libpq is the C application programmer's interface to PostgreSQL. libpq is a set of library functions that allow client programs to pass queries to the PostgreSQL backend server and to receive the results of these queries.

あとは

libpq is also the underlying engine for several other PostgreSQL application interfaces, including those written for C++, Perl, Python, Tcl and ECPG.

あたりでしょうか。要するに、Cで書かれたクライアントソフトであり、他の言語からも使用されることがある、くらいの理解を私はしています。

ということで、インストールを以下のコマンドで行います。

brew install libpq

バイナリの置かれるパスを、環境変数PATHに追加しろ、と指示がありますので、これも実行します。 環境によってはzshrcではなくzprofileが良いかもしれませんので、適宜置き換えてください。

echo 'export PATH="/opt/homebrew/opt/libpq/bin:$PATH"' >> ~/.zshrc

注意:homebrewでインストールしたバイナリがどこに置かれるかは環境によって異なりそうです。画面に表示されたコマンドを実行してください。

その後シェルの再起動、もしくは設定ファイルの再読み込みを行って、ようやくpsqlが実行できるようになるはずです。

なお、設定ファイルの再読み込みは以下です。

source ~/.zshrc

以下のコマンドでインストールされていることを確認します。

psql --version

以下のようなメッセージが出れば成功です。

psql (PostgreSQL) 15.2

接続確認

では、満を持してサーバへの接続確認を行いましょう

まずは、以下のコマンドで接続をします。

psql -h localhost -p 5432 -U postgres

続いて、パスワードを尋ねられるので、環境変数にセットしたmysecretpasswordを入力します。

以下の画面が表示されたら成功です。あとはお好きなSQLを書いて楽しみましょう。

postgres=#


GUIをインストールする

おまけで、GUIのインストール方法も書いておきます。 (実のところ、私はあまり使ったことがありません)

homebrewで以下のツールをインストールしましょう

brew install --cask pgadmin4

参考までに、homebrewのformulaeはこちら、本家のサイトはこちら

起動するとこんな感じになります。

なお、マスタパスワードの作成を求められると思いますので、適当に作成しておきましょう。

Serversを右クリック -> Register -> Serverを選んで

サーバを識別するための任意の名前と

ホスト名もしくはアドレス、そしてパスワード(上の例ではmysecretpassword)をいれます。

接続成功したのち、右クリックメニューなどでテーブルを作成できます。

その後、Create ScriptでSQLスクリプトを作成し、お好きなSQLを書いて楽しみましょう。


Pythonスクリプトを実行してみる

おまけその2として、PythonからPostgresに接続してみます。

Pythonはインストールされている前提です。

psycopg2のインストール

以下を実行して、Pythonから接続するためのツールをインストールします。

pip3 install psycopg2-binary

なお、以下のコマンドでもソースのダウンロード -> clangでビルド -> インストールできるようです。

pip3 install psycopg2

が、私の環境ではssl関連のライブラリが参照できない?などの理由でビルドに失敗しているようでした。 調べれば解決できそうでしたが、面倒だったのでビルド済みのバイナリだけを取ってくることにしました。

データを書き込む

ポケモン csv」などで検索し、ヒットしたデータを使用させていただき、データベースに格納してみたいと思います。

import psycopg2 import csv

connection = psycopg2.connect(host='localhost', user='postgres', password='mysecretpassword', database='pokemon')

with connection: with connection.cursor() as cursor:

    sql = "DROP TABLE IF EXISTS monsters;"
    cursor.execute(sql)

    sql = "CREATE TABLE monsters (\
            id serial PRIMARY KEY, \
            pokedex_number smallint, \
            name varchar(255), \
            base_total smallint, \
            attack smallint, \
            defense smallint, \
            sp_attack smallint, \
            sp_defense smallint, \
            speed smallint, \
            hp smallint \
            );"

    cursor.execute(sql)

    
    sql = "DROP TABLE IF EXISTS types;"
    cursor.execute(sql)

    sql = "CREATE TABLE types (\
            id serial PRIMARY KEY, \
            pokedex_number smallint, \
            type varchar(255) \
            );"

    cursor.execute(sql)

    
    with open('pokemon.csv') as f:
        for row in csv.reader(f):
            monsterinfo = row[:9]
            typeinfo = row[-2:]

            if monsterinfo[0] == 'pokedex_number':
                continue

            sql = "INSERT INTO monsters (pokedex_number, name, base_total, attack, defense, sp_attack, sp_defense, speed, hp) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)"
            cursor.execute(sql, monsterinfo)

            for t in typeinfo:
                if t == '':
                    continue

                sql = "INSERT INTO types (pokedex_number, type) VALUES (%s, %s)"
                cursor.execute(sql, (monsterinfo[0], t))

    
    sql = "DROP TABLE IF EXISTS affinities;"
    cursor.execute(sql)

    sql = "CREATE TABLE affinities (\
            id serial PRIMARY KEY, \
            attacker varchar(255), \
            defender varchar(255), \
            magnifier real \
            );"

    cursor.execute(sql)

    
    with open('affinity.csv') as f:
        header = []
        for i, row in enumerate(csv.reader(f)):
            if i == 0:
                header = [r.strip() for r in row]
                continue

            t0 = row[0].strip()
            for t1 in header[1:]:
                t1 = t1.strip()
                mag = float(row[header.index(t1)])

                sql = "INSERT INTO affinities (attacker, defender, magnifier) VALUES (%s, %s, %s)"

                cursor.execute(sql, (t0, t1, mag))

connection.commit()

pokemon.csvはこんな感じ。

pokedex_number,name,base_total,attack,defense,sp_attack,sp_defense,speed,hp,type1,type2 1,フシギダネ,318,49,49,65,65,45,45,grass,poison 2,フシギソウ,405,62,63,80,80,60,60,grass,poison 3,フシギバナ,625,100,123,122,120,80,80,grass,poison 4,ヒトカゲ,309,52,43,60,50,65,39,fire, 5,リザード,405,64,58,80,65,80,58,fire, 6,リザードン,634,104,78,159,115,100,78,fire,flying

以下を参考にさせていたきました。ありがとうございます。

https://gist.github.com/leoyuholo/b12f882a92a25d43cf90e29812639eb3

affinity.csvはこんな感じ。

" ",normal, fire, water, electric, grass,ice, fighting, poison, ground, flying,psychic, bug, rock,ghost, dragon,dark, steel, fairy normal,1,1,1,1,1,1,1,1,1,1,1,1,0.5,0,1,1,0.5,1 fire,1,0.5,0.5,1,2,2,1,1,1,1,1,2,0.5,1,0.5,1,2,1 water,1,2,0.5,1,0.5,1,1,1,2,1,1,1,2,1,0.5,1,1,1 electric,1,1,2,0.5,0.5,1,1,1,0,2,1,1,0.5,1,0.5,1,1,1 grass,1,0.5,2,1,0.5,1,1,0.5,2,0.5,1,0.5,2,1,0.5,1,0.5,1 ice,1,0.5,0.5,1,2,0.5,1,1,2,2,1,1,1,1,2,1,0.5,1 fighting,2,1,1,1,1,2,1,0.5,1,0.5,0.5,1,2,1,1,2,2,0.5 poison,1,1,1,1,2,1,1,0.5,0.5,1,1,1,0.5,0.5,1,1,0,2 ground,1,2,1,2,0.5,1,1,2,1,1,1,0.5,2,1,1,1,2,1 flying,1,1,1,0.5,2,1,2,1,1,1,1,2,0.5,1,1,1,0.5,1 psychic,1,1,1,1,1,1,2,2,1,1,0.5,1,1,1,1,1,0.5,1 bug,1,0.5,1,1,2,1,0.5,0.5,1,0.5,2,1,1,0.5,1,2,0.5,0.5 rock,1,2,1,1,1,2,0.5,1,0.5,2,1,2,1,1,1,1,0.5,1 ghost,0,1,1,1,1,1,1,1,1,1,2,1,1,2,1,0.5,1,1 dragon,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,0.5,0 dark,1,1,1,1,1,1,0.5,1,1,1,2,1,1,2,1,0.5,1,0.5 steel,1,0.5,0.5,0.5,1,2,1,1,1,1,1,1,2,1,1,1,0.5,2 fairy,1,0.5,1,1,1,1,2,0.5,1,1,1,1,1,1,2,2,0.5,1

こちらを使用させていただきました、ありがとうございます。

https://sironekolab.com/archives/6012

ちょっと遊んでみる

ポケモンのリストをとりあえず表示してみます。

種族値の合計が600を超えるポケモンで、ほのおタイプを持つものを表示してみたいと思います。SQL的に正しいかはよくわかりません。

ポケモンのタイプを横持ちに変換

dockerをMacに入れる方法について書いてみたいと思います。

方法は色々あるかと思いますが、私はもっぱらhomebrewしか使っていません。 これはdockerに限らず、何かソフトウェアを入れたいときはhomebrewにパッケージがあるかどうかをまずは確認し、なければ渋々dmgを探してインストールしています。

まず、homebrewが入っているかの確認をするには、ターミナルから以下を実行してください。

brew --version

入っていれば、以下のような結果が返ってくるはずです。

Homebrew 4.0.15 Homebrew/homebrew-core (git revision 830070c393f; last commit 2023-02-18) Homebrew/homebrew-cask (git revision 1a8cad145c; last commit 2023-02-18)

もし入っていなかったら以下を実行してください。

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

なお、こちらが公式サイトです。気になる方はこちらも参考にしてください。

ちなみに、パッケージの検索はこちらからできます。

Dockerのインストール

以下のコマンドをターミナルから実行してください。

brew install --cask docker

Dockerのエンジン管理にGUIを使うので、--caskオプションをつけています。

Dockerアカウントの作成

こちらからアカウントの作成をしてください。支払い情報などの入力は不要です。

Dockerエンジンの起動

dockerではエンジンとなるプロセスがまずは起動します。これを起動しておかないとdockerコマンドを実行しても成功しません。

spotlightなどからdockerを起動すると良いでしょう。

docker-spolight

docker spolight

正常に起動していれば、以下のようなアイコンがメニューバーに出ているはずです。

docker icon in menubar

docker icon in menubar

最後に動作確認をしてみます。dockerコマンドの詳細な使い方は置いておくとして、以下のコマンドで動作確認ができます。

docker run hello-world

以下の結果が出力されていれば、正常に動作しています。 また、最初の5行は手元にコンテナイメージがない場合、つまり初回などに出力されるメッセージです。

Unable to find image 'hello-world:latest' locally latest: Pulling from library/hello-world 7050e35b49f5: Pull complete Digest: sha256:4e83453afed1b4fa1a3500525091dbfca6ce1e66903fd4c01ff015dbcb1ba33e Status: Downloaded newer image for hello-world:latest

Hello from Docker! This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:

  1. The Docker client contacted the Docker daemon.
  2. The Docker daemon pulled the "hello-world" image from the Docker Hub. (arm64v8)
  3. The Docker daemon created a new container from that image which runs the executable that produces the output you are currently reading.
  4. The Docker daemon streamed that output to the Docker client, which sent it to your terminal.

To try something more ambitious, you can run an Ubuntu container with: $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID: https://hub.docker.com/

For more examples and ideas, visit: https://docs.docker.com/get-started/

ひとまず、ここまで完了すれば、dockerのインストールは完了です。