Pythonのデコレータを理解する

Pythonのデコレータを理解してみましょう。
デコレータとは関数の定義の前に@を宣言して対象の関数の機能を変化(デコレイト)させるものです。
@の後に記述した文字列の関数によってデコレータの処理が実行されます。
Javaではメソッド定義の前に@で記述するものをアノテーションと言いますが、Javaアノテーションは注記のような意味合いになり、Pythonのデコレータとは違うものになります。


この例では@some_decoratorによって関数fooがデコレイトされます。

@some_decorator  # ←これがデコレータ
def foo():
    return 1


実際にコードで確認してみましょう。

def decorate_func(func):
    print('called')

@decorate_func
def foo():
    return 1

「decorate_func」という関数を定義しています。これがデコレータになります。
「foo」という関数の定義の前に@で「decorate_func」を呼び出しています。
このコードを実行してみましょう。

>>> def decorate_func(func):
...     print('called')
...
>>> @decorate_func
... def foo():
...   return 1
...
called
>>>

「called」がコンソールに出力されました。
「decorate_func」が実行されたことが分かります。


このコードは、

@decorate_func
def foo():
    return 1

実際には下記のコードに変換され実行されることになります。

def foo():
    return 1

foo = decorate_func(foo)

Pythonでは関数もオブジェクトです。
「decorate_func」の引数として「foo」関数をオブジェクトとして渡すことが出来ます。

確認してみましょう。
「decorate_func」の中を変更します。

def decorate_func(func):
    print(type(func), func.__name__)

引数の「func」の型と名前を出力します。


実行すると、

>>> def decorate_func(func):
...     print(type(func), func.__name__)
...
>>> @decorate_func
... def foo():
...     return 1
...
<class 'function'> foo

 型は「function」、名前は「foo」と出力されました。
「foo」関数がオブジェクトとして「decorate_func」の引数に渡されて実行されたことが確認出来ます。

ただ、今のままだと「decorate_func」の戻り値がありませんので「foo」を関数として実行すると、Noneに対して関数呼び出しをする形になり「TypeError: 'NoneType' object is not callable」となります。
「decorate_func」の戻り値としてはデコレイトされた関数オブジェクトを返したいですね。


Pythonでは関数の中に関数を定義出来ます。
デコレータの中で関数を定義して、その関数をオブジェクトとして返します。

def decorate_func(func):
    def after_decorated():
        return 1 + func()  # 引数で受け取った関数オブジェクトを関数として実行して、その結果に1プラスします
    return after_decorated  # 関数をオブジェクトとして返す


以下実行してみます。

>>> def decorate_func(func):
...     def after_decorated():
...         return 1 + func()
...     return after_decorated
...
>>>
>>> @decorate_func
... def foo():
...     return 1
...
>>> print(foo())
2

1を返す「foo」の定義が「decorate_func」により2を返す関数に変化しました。

Ubuntu18.04 on WSL2のPython3.8でvenvによる仮想環境作成エラー

 「python -m venv .venv」で仮想環境作ろうとして下記エラーとなりました。

***@***:~ $ python -m venv .venv
The virtual environment was not created successfully because ensurepip is not
available. On Debian/Ubuntu systems, you need to install the python3-venv
package using the following command.

apt-get install python3-venv

You may need to use sudo with that command. After installing the python3-venv
package, recreate your virtual environment.

 

python3-venvをインストールしろと出るので下記実行しましたが、

***@***:~$ sudo apt-get install python3-venv

 状況変わらずエラー。

 

ちなみにpythonとpipは下記手順でインストール。

***@***:~$ sudo apt install -y python3.8=3.8.0-3~18.04
***@***:~$ sudo ln -s /usr/bin/python3.8 /usr/bin/python

 

***@***:~$ sudo apt-get install python3-distutils
***@***:~$ curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
***@***:~$ sudo python get-pip.py
***@***:~$ pip --version
pip 20.2.4 from /usr/local/lib/python3.8/dist-packages/pip (python 3.8)

 

 venvのインストール対象を「python3.8-venv」にしたらうまくいきました。

***@***:~$ sudo apt-get install python3.8-venv
***@***:~$ python -m venv .venv
***@***:~$ source .venv/bin/activate
(.venv) ***@***:~$

 

WSL2のUbuntu18.04でMySQL8.0を動かす

 

公式の手順に沿ってMySQL8.0をWSL2のUbuntu18.04にインストールしてもMySQLは動作しません。

試しにMySQL8をインストールしてみます。

インストール後にserviceコマンドでmysqlをstartしても「mysql: unrecognized service」と表示されてstart出来ません。

***@***:~$ service mysql start
mysql: unrecognized service

 

/usr/bin/mysqlを直接実行してもエラーになります。

***@***:~$ mysql
ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2)

 

公式手順

dev.mysql.com

 

ではどうするか。

ググったら見つけました。

Linux Native AIO interface is not supported on this platform. · Issue #3631 · microsoft/WSL · GitHub

まずMySQL5.XをインストールしてMySQL8にUpgrade、そしてスクリプトの修正とのことです。

やってみましょう。

 

 

MySQLのRepositoryを追加します。

MySQL :: A Quick Guide to Using the MySQL APT Repository

 

curlでパッケージをDownloadします。

***@***:~$ curl -LO https://dev.mysql.com/get/mysql-apt-config_0.8.15-1_all.deb
% 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 35532 100 35532 0 0 25525 0 0:00:01 0:00:01 --:--:-- 25525

 

 Downloadしたパッケージをインストールします。

***@***:~$ sudo dpkg -i ./mysql-apt-config_0.8.15-1_all.deb

 

Tabキーで「OK」にフォーカスしてEnter

f:id:fsms:20201028205554p:plain

 

5.7を選択してOK

f:id:fsms:20201029044202p:plain

 

mysql-5.7になっていることを確認してカーソルキーでフォーカスをOKに移動してEnter

f:id:fsms:20201029044334p:plain


sudo apt upateして、

***@***:~$ sudo apt update

 

インストール

***@***:~$ sudo apt install -y mysql-server

 

 MySQL5.7が起動しました。

***@***:~$ sudo service mysql start
..
* MySQL Community Server 5.7.32 is started

 

止めます。

***@***:~$ sudo service mysql stop
..
* MySQL Community Server 5.7.32 is stopped

 

 MySQLのインストール対象をMySQL8.0に変更します。

下記コマンドでconfigメニューを起動。

***@***:~$ sudo dpkg-reconfigure mysql-apt-config

 

TabキーでOKにフォーカス、Enterで進みます。

f:id:fsms:20201029082858p:plain

 

カーソルキーでmysql-8.0を選択して、TabキーでOKにフォーカスしてEnter

f:id:fsms:20201029083034p:plain

 

カーソルキーでOKを選択してEnter、メニューを終了します。

f:id:fsms:20201029083846p:plain

 

MySQL8.0にUpdateします。

***@***:~$ sudo apt update
***@***:~$ sudo apt upgrade

 

「sudo service mysql start」を実行してもエラーメッセージが表示されます。

***@***:~$ sudo service mysql start
/etc/init.d/mysql: line 40: /usr/share/mysql/mysql-helpers: No such file or directory
/etc/init.d/mysql: line 51: pathfind: command not found
/etc/init.d/mysql: line 56: get_mysql_option: command not found
/etc/init.d/mysql: line 63: get_running: command not found
/etc/init.d/mysql: line 63: [: : integer expression expected
/etc/init.d/mysql: line 67: verify_ready: command not found
/etc/init.d/mysql: line 68: verify_database: command not found
/etc/init.d/mysql: line 73: verify_server: command not found
* MySQL Community Server unknown did not start. Please check logs for more details.

 

ただ、エラーが出てもMySQLのプロセスは起動してますね。

***@***:~$ ps -ef|grep mysql
mysql 9657 6 0 09:02 ? 00:00:00 /bin/sh /usr/bin/mysqld_safe
mysql 9771 9657 8 09:02 ? 00:00:00 /usr/sbin/mysqld --basedir=/usr --datadir=/var/lib/mysql --plugin-dir=/usr/lib/mysql/plugin --log-error=/var/log/mysql/error.log --pid-file=/var/run/mysqld/mysqld.pid --socket=/var/run/mysqld
mysqld.sock

 

mysqlのプロセスを全てkillします。

***@***:~$ sudo pkill mysql

 

 

「/etc/init.d/mysql」を下記修正します。

***@***:~$ sudo vi /etc/init.d/mysql

修正前「 /usr/share/mysql/mysql-helpers」

修正後「/usr/share/mysql-8.0/mysql-helpers」

 

Diff結果

***@***:~$ diff /etc/init.d/mysql.org /etc/init.d/mysql
40c40
< . /usr/share/mysql/mysql-helpers
---
> . /usr/share/mysql-8.0/mysql-helpers

 

MySQL8.0をスタートします。

***@***:~$ sudo service mysql start
..
* MySQL Community Server 8.0.22 is started

 

 

 

1個のディストロから複数のWSL2のUbuntuインスタンスを起動する

1個のディストロから複数のWSL2のUbuntuインスタンスを起動する方法です。

「wsl -l -v」で現在の状態を見ます。Ubuntu18.04はMicrosoft Storeからインストールしています。

C:\>wsl -l -v
NAME STATE VERSION
* Ubuntu-18.04 Running 2

 

Ubuntu-18.04のディストロをexportします。 

C:\>wsl --export Ubuntu-18.04 c:\users\xxx\Ubuntu-18.04.tar

wslコマンド、--exportの後にexport対象のディストロ名、その後にフルPathの出力ファイル名をセットして実行。export完了まで少し時間がかかります。

 

次にexportされたディストロをimportします。

C:\>wsl --import Ubuntu-18.04-2 c:\users\xxx\wsl\Ubuntu-18.04-2 c:\users\mdoi\Ubuntu-18.04.tar

 wslコマンド、--importの後に任意のディストロ名、ここでは「Ubuntu-18.04-2」、その後にインストール先のフォルダPath、その後にimportするファイルPathをセットして実行。インストール先フォルダは事前に作成しておきます。

 

インストール先のフォルダに「ext4.vhdx」が作成されました。

f:id:fsms:20201029072638p:plain

 

下記コマンドでインスタンスを起動します。

c:\>wsl -d Ubuntu-18.04-2 -u ユーザー名

 

「wsl -l -v」を実行するとimport時に指定したディストロ名が追加されています。

C:\>wsl -l -v
NAME STATE VERSION
* Ubuntu-18.04 Running 2
Ubuntu-18.04-2 Running 2

 

 

importするディストロ名を変えて複数のインスタンスを同じexportファイルから作成することが出来ます。

C:\>wsl --import Ubuntu-18.04-3 c:\users\xxx\wsl\Ubuntu-18.04-3 c:\users\mdoi\Ubuntu-18.04.tar

 

Ubuntu-18.04-3が追加されました。

C:\>wsl -l -v
NAME STATE VERSION
* Ubuntu-18.04 Running 2
Ubuntu-18.04-3 Stopped 2
Ubuntu-18.04-2 Running 2

 

 

 

【参考サイト】

laboradian.com

 

stackoverflow.com

WSL2で動かすUbuntu18.04でsshサーバーを動かす

WSL2でUbuntuをインストールしたままではsshサーバーを動かせません。

sshd -t」で確認すると下記の結果となります。

***@***-wsl:~$ sshd -t
Could not load host key: /etc/ssh/ssh_host_rsa_key
Could not load host key: /etc/ssh/ssh_host_ecdsa_key
Could not load host key: /etc/ssh/ssh_host_ed25519_key

 

まず「sudo ssh-keygen -A」を実行します。

***@***-wsl:~$ sudo ssh-keygen -A
ssh-keygen: generating new host keys: RSA DSA EAAAA ED99999

 

「/etc/ssh」の下にkeyファイルが作られました。

f:id:fsms:20201017190243p:plain

 

sshサーバーを起動します。

***@***-wsl:~$ sudo systemctl start sshd

 ※systemctlを実行するためにはsystemdがPID1で動いてる必要があります。

fsms.hatenablog.com

 

sshサーバーに接続するための鍵をUbuntu上で作成します。

***@***-wsl:~$ ssh-keygen -t rsa -b 4096

 /home/ユーザー名/.ssh/の下に秘密鍵「id_rsa」、公開鍵「id_rsa.pub」が作成されます。

-rw------- 1 *** *** 3243 Oct 17 19:12 id_rsa
-rw-r--r-- 1 *** *** 753 Oct 17 19:12 id_rsa.pub

 Windowsホスト側からWSL2のUbuntussh接続出来るように「authorized_keys」に公開鍵を設定します。

id_rsa.pubの中身をauthorized_keysにコピーします。

***@***-wsl:~$ cd ~/.ssh
***@***-wsl:~$ cat id_rsa.pub > authorized_keys

 

秘密鍵Windows側のローカルディスクに保存します。

「cat id_rsa」を実行して秘密鍵の中身をコンソールに出力します。

「-----BEGIN RSA PRIVATE KEY-----」から最後の「-----END RSA PRIVATE KEY-----」をマウスで選択してコピー、ローカルディスク上にファイル名を何でもいいのですが「ubuntu.pem」を作成してそのファイルにペーストします。

***@***-wsl:~$ cat id_rsa
-----BEGIN RSA PRIVATE KEY-----
******************************************
~~~~~~~~~~~~~~~~~~~~~~~~~******************************************
-----END RSA PRIVATE KEY-----


 

これで慣れ親しんだTera TermでWSL2のUbuntussh接続出来ます(笑)。

f:id:fsms:20201018063028p:plain

 

WSL2で動くUbuntu18.04でsystemdを有効にする

WSL2でUbuntuインストール直後はPIDの「1」がMSカスタムの「init」に割り当てられています。

そのため「systemctl」による制御がエラーになります。

「systemctl」を有効にするためには「systemd」をPID1で動かす必要があります。

Ubuntuのバージョンは18.04です。

 

■systemctlでエラーとなる

***@***:~$ sudo systemctl status sshd
System has not been booted with systemd as init system (PID 1). Can't operate.

 

そのためには「genie」を使います。

github.com

 

https://github.com/arkane-systems/genie

このページの「INSTALLATION」に従ってインストールします。

 

1.daemonize, dbus, policykit-1, systemdのインストール

       それぞれ「apt install」を実行します。

 

2.dotnet-runtime-3.1のインストール

        MSの下記ページの「18.04」の章に従ってdotnetをインストールします。

docs.microsoft.com

 

3./etc/apt/sources.list.d/wsl-translinux.listの作成

***@***:~$ sudo vi /etc/apt/sources.list.d/wsl-translinux.list

 

wsl-translinux.listに下記設定

deb [trusted=yes] https://wsl-translinux.arkane-systems.net/apt/ /

 

インストール

***@***:~$ sudo apt update
***@***:~$ sudo apt install -y systemd-genie

 

インストール出来たら「genie -c bash」を実行してsystemdをPID1で動かす。

「ps aux」と叩いて確認。

f:id:fsms:20201017184257p:plain

 

「systemctl」が有効になりました。

f:id:fsms:20201017184435p:plain

 

 

Ubuntu起動時に「genie -c bash」を起動してsshサーバーを起動するように「~/.bashrc」に下記追記します。

if [ "`ps -eo pid,cmd | grep systemd | grep -v grep | sort -n -k 1 | awk 'NR==1 { print $1 }'`" != "1" ]; then
genie -c bash
sudo systemctl start sshd
fi

 

 下記参考

snowsystem.net

 

WSL2で動くUbuntuでDNSをGoogleのパブリックDNSに変更する。resolv.confの設定

WSL2で動くUbuntuDNSGoogleのパブリックDNSサービスに変更します。

WSL2でUbuntuインストール直後の/etc/resolv.confを見ると下記のようにホスト側のWindowsマシンのIPアドレスがnameserverとして設定されてます。

***@***:~$ cat /etc/resolv.conf
# This file was automatically generated by WSL. To stop automatic generation of this file, add the following entry to /etc/wsl.conf:
# [network]
# generateResolvConf = false
nameserver 172.24.192.1

 

Windows側のIPアドレスは⚙ →「ネットワークとインターネット」の中のネットワーク接続「vEthernet (WSL)」で確認出来ます。

f:id:fsms:20201017112538p:plain

 


/etc/resolv.conf は /run/resolvconf/resolv.conf へのシンボリックリンクになっているので、まずシンボリックリンクを削除します。

***@***:~$ ls -l /etc/resolv.conf
lrwxrwxrwx 1 root root 29 Oct 17 12:03 /etc/resolv.conf -> ../run/resolvconf/resolv.conf
***@***:~$ sudo rm /etc/resolv.conf

 

削除したら新規に/etc/resolv.confを作成します。

resolv.confにはnameserverとしてGoogleのパブリックDNSサービスのIPアドレス8.8.8.8を設定します。

***@***:~$ sudo vi /etc/resolv.conf
nameserver 8.8.8.8

 

このままだとUbuntuを再起動した場合に/etc/resolv.confが自動的に作成されて上書きされてしまいます。

「/etc/wsl.conf」を新規に作成して上書きされないように設定をします。

 ***@***:~$ sudo vi /etc/wsl.conf

 

 「generateResolvConf = false」を設定。

[network]
generateResolvConf = false

 

 Windows側でUbuntuを再起動してもresolv.confは上書きされずにGoogleのパブリックDNSサービスの8.8.8.8が残っています。

C:\>wsl -t Ubuntu-20.04