【Python】Flaskでcreate_app関数を作って使う
こんばんは、葛の葉です。
今回はFlaskで調べものをしているとよく見かけるcreate_app
関数を使ってみます。
環境
Python 3.6.6
Flask 1.0.2
ディレクトリ構成はこちら
カレントディレクトリ ├── app_factroy.py ├── app.py └── test_app.py
create_appを作る
# app_factroy.py from flask import Flask def create_app(config_mode='test'): app = Flask(__name__) if config_mode == 'test': app.config['DEBUG'] = True app.config['SQL_ADDRESS'] = '127.0.0.1' else: app.config['DEBUG'] = False app.config['SQL_ADDRESS'] = '69.196.1.1' return app
create_app
関数はFlask(__name__)
のインスタンスであるapp
に様々なコンフィグを与え、最終的にそのapp
を返却します。create_app
関数の引数config_mode
には開発環境か、テスト環境か、本番環境か、といった環境のステータスを渡してあげます。
create_appを呼び出す
# app.py from app_factroy import create_app app = create_app('dev') # import os # app = create_app(os.getenv('FLASK_CONFIGURE_MODE')) # 以下何らかの処理 if __name__ == __main__: app.run()
create_app
関数を呼び出して、引数に環境情報を与えます。返り値をapp
変数に代入します。そうしてapp.run()
してあげれば、Flaskは実行されます。
また、os.getenv
を使って環境変数から環境情報を与えるのもありかなぁと思います。今回のコメントアウト部分がそれです。FLASK_CONFIGURE_MODE
という環境変数内にtest
とか入れておくと、create_appの引数にtest
が入ります。いいねぇ。
下記の記事を参考にベストプラクティスなコンフを作ると良いのでは、と思います。というか、私はそうやってます。ありがとうございます。
create_appでtestする
# test_app.py from app_factroy import create_app app = create_app('test') assert '127.0.0.1' == app.config['SQL_ADDRESS']
こんな感じで呼び出してテストしてあげましょう。実行したら何も起こらずにコマンドラインが返ってくると思います。(なんかあったらテスト落ちてるって意味です。)
【Python】配列コピーについて
こんばんは、葛の葉です。
さて、Pythonの配列のコピーについて、書きます。
環境
Python3.6.6
コード達
Python 3.6.6 (default, Sep 5 2018, 03:40:52) [GCC 6.3.0 20170516] on linux Type "help", "copyright", "credits" or "license" for more information. >>> a = [1,2,3,4,5] >>> b = a >>> a [1, 2, 3, 4, 5] >>> b [1, 2, 3, 4, 5]
>>> a[0] = 6 >>> a [6, 2, 3, 4, 5] >>> b [6, 2, 3, 4, 5] #←b[0]が変更されている
>>> id(a) 140405585847624 >>> id(b) 140405585847624 >>> id(a) == id(b) True #IDの値は全く同じである。
配列の中身をコピーしているわけではなく、配列として参照しているアドレスをコピーしているため。
配列をコピーしたい場合
import copy
を用いたコピー
c
配列
>>> import copy >>> c = copy.deepcopy(a) >>> id(c) 140405578255624 >>> id(a) == id(c) False #<- IDが異なる >>> c [6, 2, 3, 4, 5]
>>> c[0] = 7 >>> c [7, 2, 3, 4, 5] >>> a [6, 2, 3, 4, 5]
>>> a [6, 2, 3, 4, 5] >>> b [6, 2, 3, 4, 5] >>> c [7, 2, 3, 4, 5] >>> a[0] = 8 >>> a [8, 2, 3, 4, 5] >>> b [8, 2, 3, 4, 5] >>> c [7, 2, 3, 4, 5]
スライス[:]
を用いたコピー
d
配列
>>> d = a[:] >>> d [8, 2, 3, 4, 5] >>> d[0] = 9 >>> d [9, 2, 3, 4, 5] >>> a [8, 2, 3, 4, 5]
>>> id(d) 140405578255688 >>> id(d) == id(a) False >>> id(d) == id(a) False
結論
リストをコピーするときは
import copy new_array = copy.deepcopy(array)
または
new_array = array[:]
【Discord】Botから役職へのメンション方法
こんばんは、葛の葉です。
ちょっと前に会社でDiscord使ってる旨お伝えしたかと存じますけど、そんな中でDiscord内の役割(権限)にメンションを飛ばしたくなりました。それが意外と日本語に乗っていなかったのでこちらに記載します。
役職にメンション飛ばすんだって
実は以下に記載の通り。でも、最初見たときよくわからなかったのでざっくりと説明すると役割に一回特殊なメンション飛ばすと、その飛ばしたときの書き込みがちょっと変わった文字<@&????????????>
みたいなものになるので、Botを使うときはその<@&????????????>
を使うようにします。
やってみた
まず役割の設定をします。サーバー
を右クリックしてサーバー設定→役割をクリック。
飛ばしたい役職を選択してこの役職に対して@メンションを許可する
をオンにして保存
どこでもいいので\@<役職名>
と送信する。今回は\@ハンプティダンプティ
そうすると送信された文字がちょっと変わった物になっている。
<@$????????????????>
みたいなものが出ていると思うので、これをそのまま書き込めばその役職へのメンションになる。
botなんかに
botなんかで役職に送信したい場合は<@$????????????????>
を文字列に付け加えてあげればOKなはず。
【GAS】Googleフォームで入力してもらった値をLogger.logに出力する
こんばんは、葛の葉です。
さて、Googleフォームという大変便利がいいWebアプリがありますね。
これとGASを連携し、GAS上のLogger.logに出力することが出来ます。
こんなGoogleフォームです
問い合わせ内容
の中身をGASで取得するようにします。
スクリプトエディタを開く
先ずはスクリプトエディタを開きます。開き方はGoogleフォームのエディタモード
から右上のその他のボタン(黒点が縦に三つ並んでいるボタン)をクリックして、出てきたリストからスクリプト エディタ
をクリックしてください。
そうしたらスクリプトエディタ画面に移行するはずです。下記のようなコードが記載されていると思います。
function myFunction() { }
コードの記入
以下のように記入し、保存してください。(Ctrl + s)など。
function myFunction(event){ var values = event.response.getItemResponses(); var value = values[0].getResponse(); Logger.log(value) }
初めて保存する際はプロジェクトの名前を問われますので、お好みで。
トリガーの設定
スクリプトエディタ画面で「現在のプロジェクトのトリガー」ボタン(時計の形をしたボタン)があります。
それをクリックしてください。そうしますと、G Suite Developper Hub
というページに飛びます。右下の「トリガーを追加」ボタンをクリックします。
実行する関数を選択
がmyFunction
になっていることを確認した後、下部にスクロールした後イベントの種類を選択
をフォーム送信時
に変更してください。
保存をすればGoogleアカウントのことを聞かれますので、そこの対応をお願いします。
問題なければここで製作は完了しています。
実際にフォームに入れてみる。
こんな内容のものを送信します。
スクリプトエディタに移動して表示
-> ログ
としてみてください。
このようにログに出力されています。
Loggerlogの部分を変えてみましょう
例えば以前作成したDiscordに送信するための関数に渡してもよいでしょう。その他のチャットツールとかでも使えるんじゃないかなぁって思います。
【Python】受け取った配列に対し、Keyが1,2,3,4と連番した数字になるような辞書を返す関数 + Unittest
こんばんは、葛の葉です。
引数に配列を渡すと、連番をKeyにした辞書型を返してくれる関数を作りました。必要かどうかわかんないけど、一時、入用だったのですが、多分もういらない関数なのでここで供養させてください。
こんなんなのよ
関数numbering_dictionary()
の引数に['nezumi', 'ushi', 'tora', 'usagi']
のような配列を渡すと{1: 'nezumi', 2: 'ushi', 3: 'tora', 4: 'usagi'}
という辞書を返します。だからなんだってレベルのものですな。
関数はこんな感じ
helper_def.py
という名前で以下のコードを保存してください。
def numbering_dictionary(arry_choices_answer, start_key=1): ''' 受け取った配列に対し、Keyが1,2,3,4と連番した数字になるような辞書を返します。 ex: numbering_dictionary(['nezumi','ushi','tora']) -> {1: 'nemuzi', 2: 'ushi', 3: 'tora'} start_key(int)は連番開始の数字になります。デフォルトは1。 ''' numbring_dictionary = {} for index_number, choices_answer in enumerate(arry_choices_answer, start=start_key): numbring_dictionary[index_number] = choices_answer return numbring_dictionary
Unittestはこんな感じ
Unittest用プログラミングは以下になります。test_helper_def.py
みたいな名前でhelper_def.py
と同じところに保存してください。なお、動作させるだけならこのファイルは要らないです。
import pytest from . import helper_def def test_numbering_dictionary(): array1 = ['nezumi', 'ushi', 'tora', 'usagi'] array2 = ['serval', 'araisan', 'vulpes_zerda', 'silverfox'] array3 = ['one', 'two', 'three', 'four'] assert {1: 'nezumi', 2: 'ushi', 3: 'tora', 4: 'usagi'} == helper_def.numbering_dictionary(array1) assert {1: 'serval', 2: 'araisan', 3: 'vulpes_zerda', 4: 'silverfox'} == helper_def.numbering_dictionary(array2) assert {2: 'one', 3: 'two', 4: 'three', 5: 'four'} == helper_def.numbering_dictionary(array3, start_key=2)
【kubernetes】SecretとRedisのパスワード設定
こんばんは、葛の葉です。
さて、今回はkubernetesでRedisをデプロイする際のパスワードの設定とSecretの設定方法を記載します。
環境
minikube ver 0.31.0
Redis(image) tag 5.0.3
簡単なRedisのパスワード設定のおさらい
Redisがインストールされている環境であれば、以下のコマンドでパスワードを設定できます。
redis-server --requirepass [password]
コマンドを打つとRedisが起動して、以下のような感じでメッセージが出力されると思います。今回のパスワードは1212
です。
root@657cb0734634:/data# redis-server --requirepass 1212 15:C 20 Feb 2019 04:05:48.917 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo 15:C 20 Feb 2019 04:05:48.918 # Redis version=5.0.3, bits=64, commit=00000000, modified=0, pid=15, just started 15:C 20 Feb 2019 04:05:48.918 # Configuration loaded _._ _.-``__ ''-._ _.-`` `. `_. ''-._ Redis 5.0.3 (00000000/0) 64 bit .-`` .-```. ```\/ _.,_ ''-._ ( ' , .-` | `, ) Running in standalone mode |`-._`-...-` __...-.``-._|'` _.-'| Port: 6379 | `-._ `._ / _.-' | PID: 15 `-._ `-._ `-./ _.-' _.-' |`-._`-._ `-.__.-' _.-'_.-'| | `-._`-._ _.-'_.-' | http://redis.io `-._ `-._`-.__.-'_.-' _.-' |`-._`-._ `-.__.-' _.-'_.-'| | `-._`-._ _.-'_.-' | `-._ `-._`-.__.-'_.-' _.-' `-._ `-.__.-' _.-' `-._ _.-' `-.__.-' 15:M 20 Feb 2019 04:05:48.919 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128. 15:M 20 Feb 2019 04:05:48.920 # Server initialized 15:M 20 Feb 2019 04:05:48.920 * DB loaded from disk: 0.000 seconds 15:M 20 Feb 2019 04:05:48.920 * Ready to accept connections
パスワードが設定されれば、そのRedisに接続したあと、操作を行うにはパスワードが必要になります。例えばredis-cli
で接続した際は、auth [password]
といった形で認証を得ないといけません。
root@657cb0734634:/data# redis-cli -h 192.168.99.100 -p 6379 192.168.99.100:6379 > auth 1111 ERR invalid password 192.168.99.100:6379 > auth 1212 OK
ERR invalid password
と出るとパスワードが間違っています。OK
が出てれば接続できます。ここまでがRedisでパスワードを設定する方法とそのRedisに繋げる方法です。
KubernetesのSecret
さて、KubernetesのSecretですが、詳細は以下のリンクを見てもらえればと思います。
KubernetesではこのSecretというオブジェクトを使ってパスワードを管理したほうが良いようです。先ほどの1212
というパスワードを管理するSecretを作成します。
ファイル名test-secret.yaml
apiVersion: v1 kind: Secret metadata: name: redis-password # secretの名称となり、k8s上で使用する名前 type: Opaque data: RedisPassword: MTIxMg== #base64でないと受け付けません。
metadata
のname
を使って、このSecretをredis-password
と命名し、Kubernetes内ではこの名前でこのSecretを呼び出します。また、data
内にあるRedisPassword
とMTIxMg==
はKeyとValueになっています。MTIxMg==
はbase64で1212
になります。Keyは複数作ることが出来ますので、一つのSecretに複数のパスワード等を含めることが出来ます。例えばusername
keyとpassword
keyを入れるなんてことも出来ます。
kubectl apply -f test-secret.yaml
とすれば設定が反映されます。
kubectl get secret
とすればSecretが表示されます。
NAME TYPE DATA AGE default-token-57h4x kubernetes.io/service-account-token 3 5d redis-password Opaque 1 28m
default-token-57h4x
はminikubeのものかなぁと。
Podの設定はこんな感じ
次にKubernetes上でRedisの入ったPodを作成します。今回はSecretの設定はPodで行いますが、DeployMentやStatefulSetでも設定可能です。
ファイル名test-pod.yaml
apiVersion: v1 kind: Pod metadata: name: test-redis labels: app: test-redis #ここの値を用いてServiceと連携する spec: containers: - name: test-redis-ctr image: redis:5.0.3 env: - name: REDIS_PASSWORD # Pod内に挿入する環境変数の名前 valueFrom: secretKeyRef: name: redis-password # 先ほど作成したSecret名 key: RedisPassword # 先ほど作成したKey名。Valueが環境変数内に代入される。 command: ["redis-server"] args: ["--requirepass $(REDIS_PASSWORD)"] ports: - containerPort: 6379
yaml内のspec
以下にcontainers
項目があって、これらが生成するコンテナの詳細に設定する部分になります。-name
はコンテナの名前です。image
はそのPodを作成するに使用するDockerimage
を記します。
また、env
があって、これがPod内の環境変数を作成する項目になります。env
内-name
は環境変数名です。 valueFrom
はその環境変数の値を引っ張ってくる設定ファイルがどの形式であるかというもので、Secretを使用する場合はsecretKeyRef
とします。configMapKeyRef
だとConfigMap
というオブジェクトを使用するようになります。name
はSecretの名前を、key
はSecret内data
で記入したkeyであるRedisPassword
にします。
command
はPod生成時に実行されるコマンドで、redis-server
というコマンドを、args
で引数を渡しています。--requirepass
は先に説明したとおりで、$(REDIS_PASSWORD)
が環境変数部分になります。ブランケットの括弧${}
ではなくパレンティスの括弧$()
なので注意してください。
kubectl apply -f test-pod.yaml
で作成しましょう。
kubectl get pods
で出来たPodを見られます。
NAME READY STATUS RESTARTS AGE test-redis 1/1 Running 0 34m
なお、STATUSがErrImagePull
といったものであった場合は、Redis:5.0.3のイメージが存在していないので作成できていないというエラーになります。minikube
でしたらdocker pull redis:5.0.3
とかやってみてください。
Serviceを使ってみる(minikube)
次にServiceを使って先のPodを確認出来るようにします。
ファイル名test-svc.yaml
apiVersion: v1 # ここも固定。kubernetes側が対応したらVの値を増やす形に kind: Service metadata: name: test-service # Serviceそのものの名前。アンスコが使えない等ルールあり spec: type: NodePort selector: app: test-redis ports: - port: 6379 targetPort: 6379
Serviceの説明は省きます。kubectl apply -f test-svc.yaml
で作成し、kubectl get service (又は"svc")
と打って確認しましょう。
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE test-service NodePort 10.111.68.208 <none> 6379:30271/TCP 38m
CLUSTER-IP
やPORT(S)
のコロン右側の値はそれぞれ異なると思います。
IPアドレスとポート番号を確認する
GKEと違って、minikubeではNodePortタイプ(LoadBalancerタイプも同様)のServiceにEXTERNAL-IP
は振られないようです。代わりに、minikubeの下記のコマンドでIPアドレスを確認する必要があります。
minikube service [Service名] --url
今回はminikube service test-service --url
です。そうしますと、http://192.168.99.100:30271
といった表示が出てくると思います。IPアドレス部分、Port番号部分はそれぞれ異なると思います。
C:\>minikube service test-service --url http://192.168.99.100:30271
これが今回生成したServiceにアクセスするためのIPアドレスとPort番号になります。
redis-cliでアクセス
下記コマンドを打ちます。
redis-cli -h [IPアドレス] -p [Port番号]
root@kuzunohasan:# redis-cli -h 192.168.99.100 -p 30271 192.168.99.100:30271> get 1 (error) NOAUTH Authentication required. 192.168.99.100:30271> auth 1212 OK 192.168.99.100:30271> get 1 (nil)
最初のget 1
は(error) NOAUTH Authentication required.
と弾かれていますが、auth 1212
とした後、get 1
とした場合は(nil)
とされています。無事成功しています。
綺麗にする
kubectl apply -f ***.yaml
で設定したものはkubectl delete -f ***.yaml
で削除可能です。
また、apply
でもそうですが、ディレクトリを選択すれば、ディレクトリ内のyamlファイル全てを読みに行き、全てを削除してくれます。
C:\>kubectl delete -f test-secret.yaml secret "redis-password" deleted C:\>kubectl delete -f test-pod.yaml pod "test-redis" deleted C:\>kubectl delete -f test-svc.yaml service "test-service" deleted
なんでRedisConfでパスワードの設定しないの?
今回のRedisのパスワードの設定はサーバー実行コマンドのオプションを使っていて、confを使った設定は行っていません。なぜならconf内で環境変数を呼び出して取得する方法がなかったからです…シェルスクリプトを使ったらよいかもしれませんね。
【Python】 値が文字列の中に含まれているか確認する。
こんばんは、葛の葉です。
さて、標題の件になりますが、ちょっと言葉だと説明しにくいかなと思います。例を挙げると
I Have a Dream.
Wikipediaより引用
この中に特定の文字が入っていないかをBoolで返してもらう方法を書きます。
inを使う
例えばa
という単語が入っているかを確認するには以下のようにします。
'a' in 'I Have a Dream'
以下がコマンドラインでPythonを実行したときの結果です。
>>> 'a' in 'I Have a Dream' True >>> 'b' in 'I Have a Dream' False
Boolで返るのでassert
も使えます。
>>> assert 'a' in 'I Have a Dream' >>> assert 'b' in 'I Have a Dream' Traceback (most recent call last): File "<stdin>", line 1, in <module> AssertionError
assert 'a' in 'I Have a Dream'
は特に問題がないためTrueとなり、エラーなどは発生していません。一方、assert 'b' in 'I Have a Dream'
はFalseとなるため、AssertionErrorとなっています。
listに入った複数の文字列が該当の文字列内に含まれているかの確認
例えば以下のように、I
とHave
とa
の文字全てが該当の文字列に含まれているかを確認したいとします。しかし、下記のようにリストをin
としてもTypeErrorが返されます。
>>> ['I', 'Have', 'a'] in 'I Have a Dream' Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: 'in <string>' requires string as left operand, not list
リスト内包表記を使う
というわけでリスト内包表記を使いましょう。
False not in [i in 文字列 for i in 検索したい文字列の入ったリスト]
コマンドラインでは以下のようになります。
>>> False not in [i in 'I Have a Dream' for i in ['I', 'Have', 'a']] True
分解するとFalse not in [リスト]
と[i in 'I Have a Dream' for i in ['I', 'Have', 'a']]
になると思います。
False not in [リスト]
は最初にお話した'a' in 'I Have a Dream'
の応用で、[list]の中にFalseが無ければTrueとなります。すなわち、リスト内が全てTrueだとTrueを返し、一つでもFalseがあればFalseを返します。ヤヤコシイネ。
>>> False not in [True,True,True,True] True >>> False not in [True,True,True,False] False >>> False not in [False,False,False,False] False
さて、[i in 'I Have a Dream' for i in ['I', 'Have', 'a']]
ですが、こちらは下部と同じ結果になります。
return_list = [] for i in ['I', 'Have', 'a']: result = i in 'I Have a Dream' return_list.append(result) return_lisrt [True, True, True]
結果は[True, True, True]
となります。
すなわち、for文を用いて、含まれているか確認したい文字列が入ったリストの要素を一つずつとりだし、それぞれin
を用いてBool値を返してもらい、それをリストの中に格納しています。
リスト内包表記での結果はこちら。
[i in 'I Have a Dream' for i in ['I', 'Have', 'a']] [True, True, True]
含まれていない文字があればFalseがリストに返されます。
>>> [i in 'I Have a Dream' for i in ['I', 'king', 'a']] [True, False, True] # kingの文字は存在しない
上記のFalse not in
と組み合わせると
>>> False not in [i in 'I Have a Dream' for i in ['I', 'Have', 'a']] True >>> False not in [i in 'I Have a Dream' for i in ['I', 'king', 'a']] False
と、このようになるわけです。
assertではこうです。
>>> assert False not in [i in 'I Have a Dream' for i in ['I', 'Have', 'a']] >>> assert False not in [i in 'I Have a Dream' for i in ['I', 'king', 'a']] Traceback (most recent call last): File "<stdin>", line 1, in <module> AssertionError
関数はこちら
def bool_check_words_in_word(check_words: list, the_word: str): # check_wordsには確認したい文字が含まれているリストを入れてください。 # the_wordにはチェックしたい文字列を入れてください。 return False not in [check_word in the_word for check_word in check_words]
print( bool_check_words_in_word( check_words=['I', 'Have', 'a'], the_word='I Have a Dream') ) True