自分自身のブログを読み返す話

ブログを書くようになってから、それなりに時間が経った。過去に自分で Movable Type をホストしていたときの記事や、はてなダイアリーに書いたりしたものは消えてしまったけど、それでも 2012 年から先の記事はまだ生きている。 ところで、僕のブログ記事をもっとも楽しく読めるのは自分自身なのではないかと思う。調べ物をしているときに検索エンジンが導いてくれるページはしばしば過去の自分のブログ記事だし、何より自分の記事は読みやすい。「過去の自分が名文を書いたから」ではなく、記事を理解するのに必要な前提知識があるからである (なにせ書き手が自分自身なので)。 自分がメンテナンスしているブログの sitemap.xml をパースしてリダイレクトするプログラムを Heroku で動かし、それをブラウザの新規タブのデフォルトページに設定したこともある。 新しいタブを開いたとき、自分のブログの中からページをランダムに選んで表示するようにしてみた。 — ヾ(✿❛◡❛)ノ Yasu 🤡 (@mahata) January 15, 2021 ただし、これだと新規タブを開くのが遅くなってしまう。よりよい方法はないかと模索している。 現在の暫定解は macOS の Launchd を使い、定期的にブログ記事をランダムに選んで Slack にポストするというものだ。 具体的には $HOME/LaunchAgents/slack-reminder.plist を作り、次のように記述した。 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Label</key> <string>Slack Notifier 1 hour</string> <key>ProgramArguments</key> <array> <string>/Users/mahata/.pyenv/shims/python</string> <string>/Users/mahata/bin/slack-post.py</string> </array> <key>StartInterval</key> <integer>3600</integer> <key>StandardOutPath</key> <string>/tmp/slack-post.out</string> <key>StandardErrorPath</key> <string>/tmp/slack-post.err</string> </dict> </plist> 雰囲気で読めそうな気がするので詳細は割愛するけれど、これで 3600 秒ごと (つまり 1 時間ごと) に次のコマンドを実行する、と指定している。 [Read More]

LeetCode の Number of Matching Subsequences の解答例 (Python 3)

LeetCode の Number of Matching Subsequences を Python 3 で解いてみた。説明の一部が Python 風の擬似コードになってしまったものの、雰囲気で読めるはず。 直感的なアイディアの説明 words を「先頭文字をキー、残りの文字列」をバリューとして辞書型のオブジェクトに保存する。問題文のサンプルである words = ["a", "bb", "acd", "ace"] の場合は、次の辞書オブジェクトを作る (以下 dic と呼ぶ)。 { 'a': ['', 'cd', 'ce'], 'b': ['b'] } 対応する S = "abcde" を走査しながら words 中の Subsequences を数えるにはどうすればよいだろうか。ひとつの案として dic を作り変えながら Subsequence を数え上げる方法がある。具体的には次のようにする。 前準備として ans = 0 と初期化する (Subsequence を数え上げるために使う変数) S[0] で dic を引くと ['', 'cd', 'ce'] というリストが返る (リストを取得後に dic[S[0]] をいったん削除する) 取得したリストに含まれる文字列を順に調べていく (以下、リストに含まれるそれぞれの文字列を word と呼ぶ): word が空文字であれば Subsequence を発見したことと等しいので ans += 1 する word が空文字でなければ dic[word[0]] に word[1:] を追加する S[1] で dic を引くと ['b'] というリストが返る (リストを取得後に dic[S[1]] をいったん削除する) 取得したリストに含まれる文字列を順に調べていく (以下、リストに含まれるそれぞれの文字列を word と呼ぶ): word が空文字であれば Subsequence を発見したことと等しいので ans += 1 する word が空文字でなければ dic[word[0]] に word[1:] を追加する S[2] で dic を引くと ['d', 'e'] というリストが返る (リストを取得後に dic[S[2]] をいったん削除する) … 以下略 (S[len(S)-1] まで同様の処理を繰り返す) コード 次のようにコードを書いた。このコードは Runtime: 576 ms, faster than 61. [Read More]

LeetCode の Shortest Way to Form String の解答例 (Python 3)

LeetCode の Shortest Way to Form String を Python 3 で解いてみた。なお、基本的なアイディアは Twoho さんの Discussion へのポスト を参考にしている。 直感的なアイディアの説明 次の入力を考えてみよう。 source: "abcab" target: "aabbaac" まず source から、次のデータ構造を作り出す。 { 'a': [0, 3], 'b': [1, 4], 'c': [2] } これは「キーがアルファベット、値が出現位置のリスト」な辞書である。 次に具体的な処理について考えよう。まず source のどこに注目しているかを示すインデックスを i とする (初期値: 0)。また source を何巡したか示す変数を cnt_loop とする (初期値: 1)。cnt_loop が最終的な解となる。なぜなら、source を巡回する数が minimum number of subsequences そのものになるからである。 ここまでを前準備として、target を走査しながら次の処理をしていく。target 中で注目している文字を c とする。 最初の c は target[0] すなわち 'a' である。これで辞書を引くと [0, 3] である。このリストで i 以上の最小の値 は 0 なのでインデックスをひとつ進めて (0 + 1 して) i = 1 とする。 次の c は target[1] すなわち a である。これで辞書を引くと [0, 3] である。このリストで i 以上の最小の値 は 3 なのでインデックスをひとつ進めて (3 + 1 して) i = 4 とする。 次の c は target[2] すなわち b である。これで辞書を引くと [1, 4] である。このリストで i 以上の最小の値 は 4 なのでインデックスをひとつ進めて (4 + 1 して) i = 5 とする。 次の c は target[3] すなわち b である。これで辞書を引くと [1, 4] である。このリストで i 以上の最小の値 は存在しないので、cnt_loop += 1 しつつ i をリストの最小の要素 + 1 (つまり 1 + 1 = 2) にする。 次の c は target[4] すなわち b である。これで辞書を引くと [1, 4] である。このリストで i 以上の最小の値 は 4 なのでインデックスをひとつ進めて (4 + 1 して) i = 5 とする。 … (省略) … このようにしていくと、最終的に cnt_loop は 3 となる。 [Read More]

LeetCode の Delete Nodes And Return Forest の解答例 (Python 3)

LeetCode の Delete Nodes And Return Forest を Python 3 で解いてみた。 アルゴリズム 僕は木を探索しながら返り値を作り上げていくような実装をした。 具体的には、再帰関数 (walk_tree) に木の頂点を渡し、そこから子ノードがある限り自分自身をコールしていく。 再帰関数には has_parent という引数を設けて、親ノードがある場合とない場合で処理を分岐させる。親ノードがない場合 (i.e., has_parent = False の場合) はルートノードなので、返り値に自分自身を追加する。 木をたどる中で、ノードの値が to_delete に含まれる場合は has_parent = False として再帰関数をコールする。また 親を持たないノードはルート なので、そのノードは返り値のリストに append する。 コード 具体的なコードは次の通り。この実装で Runtime: 68 ms, faster than 66.80% of Python3 online submissions for Delete Nodes And Return Forest. とのこと。なお、同じ実装で faster than 85.44% of Python3 online submissions になることもあるので、多くの そこそこ速い 実装は似ているのだと思う。 ブログの読者向けに詳細にコメントを書き込んだコードを貼り付けておく。 from typing import List class TreeNode: def __init__(self, x): self. [Read More]

LeetCode の Decode String の解答例 (Python 3)

LeetCode の Decode String を Python 3 で解いてみた。 アルゴリズム tl;dr スタックを使って与えられた文字列をパースしながら、出力文字列を組み立てた。具体的には次の通りである。 与えられた文字列を左から走査する。[ を発見するたび、そこまでに現れた数字とアルファベットをスタックにプッシュする。] を見つけたらスタックから値をふたつポップして (それぞれ prev_num と prev_str という名前と仮定する) cur_str = prev_str + cur_str * int(prev_num) として文字列を組み立てる。ここでの右辺の cur_str は現時点までに現れたアルファベットである。 時間計算量は O(n) と言ってよいはず。与えられた文字列を走査 (O(n)) しながら処理 (O(1)) をするため。 具体的なコードは次の通り。この実装で Runtime: 24 ms, faster than 84.71% of Python3 online submissions for Decode String. とのこと。 #!/usr/bin/env python3 # -*- coding: utf-8 -*- # Difficulty: Medium # https://leetcode.com/problems/decode-string/ class Solution: def decodeString(self, string: str) -> str: stack = [] cur_num = '' cur_str = '' for char in string: # 入力文字は '[', ']', 数字, アルファベットの 4 パターンに分けて考える if char == '[': stack. [Read More]

プライベートサブネットの Locust クラスタで動いているウェブインタフェースに接続する

Locust のようなウェブアプリケーションに対するベンチマークツール (負荷試験ツール) を使うのであれば、アプリケーションが動いている場所と同じネットワークで起動するべきである。ベンチマークを取るときにネットワークの影響を軽減するためだ。

多くの場合はウェブアプリケーションはプライベートサブネット内で動くだろう。公開されたロードバランサが、ウェブアプリケーションにリクエストをフォワードするのである。この場合、Locust も同じプライベートサブネットで動かすべきである。しかし、Locust のウェブインタフェースに手元からアクセスしたい場合はどうすればよいだろう?

この場合、SSH のポートフォワードを使うのが最も手っ取り早い。

Locust のウェブインタフェース (通常はマスター) が ec2-xx-xx-xx-xx.ap-northeast-1.compute.amazonaws.com のポート 8089 (デフォルト) で動いているものとする。また、このプライベートサブネットに接続できる踏み台サーバが bastion.example.com と仮定する。その場合、次のコマンドでリモートで動いている Locust のウェブインタフェースに対してポートフォワードできる。

ssh -fNL 8081:ec2-xx-xx-xx-xx.ap-northeast-1.compute.amazonaws.com:8089 bastion.example.com

このコマンドを叩いた後で localhost:8081 をブラウザで開けば、手元のブラウザから Locust を管理できる。

Python で Socks プロキシを経由して目的とするサーバにアクセスする方法

この記事は何か?

Python で Socks プロキシを経由して目的とするサーバにアクセスする方法について書いた記事である。わたしは Python 3.8 環境で実験したけれど、Python 3 系であればバージョンを問わず同じように動かせるはずである。

具体的なコード

requestsPySocks が導入された状態で、次のようにプロキシの設定をする。なお、Socks プロキシそのものを作成する方法はこちらの記事を参考にしてほしい。

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import requests

proxies = {
    'http' : "socks5h://localhost:1080",
    'https' : "socks5h://localhost:1080"
}

requests.get('http://somewhere.example.internal/ping', proxies=proxies)

何がハマりどころかというと、Socks プロキシの記述で socks5://localhost:1080 ではなく、socks5h://localhost:1080 としなければならない点である。そうでない場合、DNS での名前解決をローカルで行うため、Failed to establish a new connection: [Errno 8] nodename nor servname provided, or not known のようなエラーメッセージを見ることになる。

Pootle 2.5.0 のインストール

需要は大きくないだろうけど、とりあえず書く。

まず現時点で GitHub の INSTALL ファイルに記載されている情報 は間違いである。Pootle の最新版を解凍しても PootleServer ファイルは存在しない。2.1.6 や 2.2.0 のスナップショット版にはこのファイルが存在している。プログラムのアップデートにドキュメントのアップデートがついてきていないようである。

代わりに解凍されたファイルには setup.py がついてきている。これを実行する。

$ python setup.py install

これで終わりではなく、pootle ディレクトリに移動して次の操作を行う。

$ mkdir -p pootle/assets
$ python manage.py collectstatic --noinput
$ python manage.py assets build

これらは fabfiles.py に記述されている。けど fabfiles.py がいつ走るべきかよく分からない…ので手作業で実行する。

最後に manage.py を走らせる。

$ python manage.py runserver

これで localhost の 8000 番ポートで Pootle が起動する。