社内se × プログラマ × ビッグデータ

プログラミングなどITに興味があります。

python 0埋め 削除する

datetime の日付0埋めを削除する方法については、検索したら解説が見つかります。
普通の文字列、例えば "0000120" を "120" にするにはどうすれば良いのだろう?

"python 0埋め 除去(削除、解除)" など色んなキーワードで検索したけどヒットしない。
除去する桁数が決まっていたら、format とか使えば出来るのかとか難しく考えていましたが。。

num = "0000120"
print int(num)

試してみたら、これで除去してくれました。
常識なこと?であるため、検索してもヒットしなかったのかもしれません。

確かに、Java でもこんな感じでいけますしね。
正直、正攻法なのかは分かってません。

Integer.valueOf("0000120")

もし文字列 "120" にしたいのなら、str(int(num)) で。

ERROR: The node /hbase-unsecure is not in ZooKeeper.

エラー内容

Docker で HBase の Container を作っているのですが、hbase shell を起動した時に発生したエラー。
Docker とは何の関係もないエラーです。

ERROR: The node /hbase-unsecure is not in ZooKeeper. It should have been written by the master. Check the value configured in 'zookeeper.znode.parent'. There could be a mismatch with the one configured in the master.

HBase のMasterサーバー(HMaster)を起動すると、zookeeper 内に /hbase-unsecure が生成されるはずなのですが、それが作られていないよというエラー。
実際に、zkCli.sh で除いてみても、このノードは生成されていませんでした。

調査

HMaster が起動してないのかも?ということで、ログを見てみると以下のエラーが出て起動できていませんでした。

HMaster: Failed to become active master SIMPLE authentication is not enabled

このエラーは見た記憶がなかったのですが、以下に回答がありました。
https://stackoverflow.com/questions/35975094/master-hmaster-failed-to-become-active-master-simple-authentication-is-not-enab?rq=1
core-site.xml と hbase-site.xml で指定しているポート番号が一致していないだろうとのこと。

確認してみると、一致してませんでしたので、9000 で揃えました。
(HDP のドキュメント見ながら設定したはずなのに。。。)

core-site.xml

<property>
    <name>fs.defaultFS</name>
    <value>hdfs://hdp-resource.docker:9000</value>
</property>

hbase-site.xml

<property>
    <name>hbase.rootdir</name>
    <value>hdfs://hdp-resource.docker:9000/apps/hbase/data</value>
</property>
確認

改めて、HMaster を起動、RegionServer も起動してます。

/usr/hdp/current/hbase-master/bin/hbase-daemon.sh start master
/usr/hdp/current/hbase-regionserver/bin/hbase-daemon.sh start regionserver

今度は hbase shell も問題なく動作しました。

hbase(main):001:0> list
TABLE                                                                                                                                                                                             
0 row(s) in 0.5440 seconds

=> []
hbase(main):002:0> create_namespace 'ns'
0 row(s) in 0.0840 seconds

hbase(main):003:0> list_namespace
NAMESPACE                                                                                                                                                                                         
default                                                                                                                                                                                           
hbase                                                                                                                                                                                             
ns                                                                                                                                                                                                
3 row(s) in 0.1300 seconds

色んなモジュールが連携していることもあって、設定ミスなど、はまりポイントが多いです。

Unity Unable to list target platforms

Unity で作成したゲームを Android の実機にビルドしようとしたら、以下のエラーが発生。

Unable to list target platforms.

f:id:blueskyarea:20180701095839p:plain

コンソールには以下のエラーが表示されているが、意味が分からない。
f:id:blueskyarea:20180701095921p:plain

確かにしばらく Unity を触っていなかったけど、以前は問題なくビルドできていた。
メッセージが言うとおり、Android SDKのパスを確認してみたけど問題なさそう。

Unity 側

f:id:blueskyarea:20180701100417p:plain

調べてみると、どうやら Android Studio のアップデートによる影響らしい。
https://answers.unity.com/questions/1320634/unable-to-list-target-platforms-when-i-try-to-buil.html

記事のとおり、tools フォルダ毎入れ替えたらビルドが通るようになりました。
そういえば、kotlin でアプリを作る時に、Android Studio のアップデートをしたような気がします。
となると、今度は kotlin のプログラミングが上手く動作しなくなっているかも。。
開発環境に依存関係を持たせたくないので、VM などして、開発環境を分けた方がいいかもしれない。

JMockito 一部のメソッドのみをモック化する(Java モック)

これの JMockito 版です。
blueskyarea.hatenablog.com

モック化するクラス
public class PointCard {
	
	public PointCard() {
	}
	
	public String getMemberName() {
		return "hoge";
	}
	
	public long getPoint() {
		return 1000L;
	}
}
テスト対象のクラス
public class Member {
	private PointCard pointCard;
	
	public Member() {
		this.pointCard = new PointCard();
	}

	public String getMemberNameAndPoint() {
		return pointCard.getMemberName() + "_" + pointCard.getPoint();
	}
}
テスト
@RunWith(JMockit.class)
public class MemberTest {
	@Test
	public void testGetPointIsMocked() {
	    new NonStrictExpectations(PointCard.class) {  // 1
                {
                    new PointCard().getPoint();  // 2
                    result = 7777L;  // 3
                }
            };
            Member member = new Member();
            assertThat(member.getMemberNameAndPoint(), is("hoge_7777"));  // 4
	}
}

1. Expectations(NonStrictExpectations) のコンストラクタに、モック化するクラスの Class オブジェクトを渡す。
2. モック化したいメソッドを記述する。
3. そのモックの振る舞いを記述する。
4. 結果を評価する。
今回、PointCard クラスのメソッドにおいて、
getMemberName() はモック化していないため、"hoge" がそのまま返却される。
getPoint() はモック化していて、7777 を返却するように定義しているため、7777 が返却される。
よって結果は、"hoge_7777" となる。

これをもし、以下のように書いた場合、

@RunWith(JMockit.class)
public class MemberTest {
	@Test
	public void testGetPointIsMocked(@Mocked PointCard pointCard) {
	    new NonStrictExpectations() {
                {
            	    pointCard.getPoint();
                    result = 7777L;
                }
            };
            Member member = new Member();
            assertThat(member.getMemberNameAndPoint(), is("hoge_7777"));
	}
}

getMemberName() もモック化されてしまう。
そして振る舞いを定義していないので、null が返却される。
よって結果は、"null_7777" となる。

所感

Mockito で同じことを実現したいときは、@Spy や @InjectMocks を定義してあげる必要がありました。
JMockito では特別なアノテーションも必要ないので、これに関しては、JMockito の方がシンプルに書けて良いなと思います。

JMockito 引数に応じて返す値を変化させる(Java モック)

これの JMockito 版です。
blueskyarea.hatenablog.com

テストコード(JUnit)
@RunWith(JMockit.class)
public class BirthMonthTest {
	// Without mock
	@Test
	public void testGetBirthStoneWithoutMock() {
		BirthMonth birthMonth = new BirthMonth(2);
		assertThat(birthMonth.getBirthStone(2), is(nullValue()));
		assertThat(birthMonth.getBirthStone(7), is(nullValue()));
		assertThat(birthMonth.getBirthStone(11), is(nullValue()));
	}
	
	// With mock
	@Test
	public void testGetBirthStoneWithMock(@Mocked BirthMonth birthMonth) {
	    new NonStrictExpectations() {
              {
            	birthMonth.getBirthStone(2);
            	result = "amethyst";
            	birthMonth.getBirthStone(7);
            	result = "ruby";
            	birthMonth.getBirthStone(11);
            	result = "topaz";
            	birthMonth.getBirthStone(anyInt);
            	result = "not found";
              }
	  };
		
          // exception if not "NonStrictExpectations".
	  assertThat(birthMonth.getBirthStone(5), is("not found"));

	  assertThat(birthMonth.getBirthStone(2), is("amethyst"));
	  assertThat(birthMonth.getBirthStone(7), is("ruby"));
	  assertThat(birthMonth.getBirthStone(11), is("topaz"));
	  assertThat(birthMonth.getBirthStone(20), is("not found"));
		
	  // exception if not "NonStrictExpectations".
	  assertThat(birthMonth.getBirthStone(30), is("not found"));
	}
}

Expectations を使用した場合、定義した振る舞いの順番どおりに、そのメソッドが呼ばれることが期待される。
この例では、まず最初に 2 が引数として与えられた場合の振る舞いが定義されている。
ただ、assertThat では最初に 5 が引数として与えられることになっているため、この時点でエラーになる(expect 2, but 5)。
また、最後の assertThat では 30 を引数として与えているが、定義した振る舞いの anyInt は、その前の引数 20 に対して適用されるため(使用済みのような扱い)、30 の場合の振る舞いが定義していないということでエラーになる。
いずれの場合も以下のエラー。

UnexpectedInvocation: Unexpected invocation of:

NonStrictExpectations を使用してあげることで、この辺りの制約がゆるくなり、定義した振る舞いの数が呼び出しの数よりも少なかったとしても、anyInt で定義した振る舞いが適用されるか、もし anyInt を定義していなければ、null(初期値) が返却されるような動作になる。

機能的には問題ないと思うのですが、この辺りについては Mockito の方が若干分かり易い(書き方)気がします。

Mockito の場合。

when(birthMonth.getBirthStone(2)).thenReturn("amethyst");

JMockito でも、無理やり一行で書けますが。。

birthMonth.getBirthStone(2); result = "amethyst";

JMockito あるメソッドが任意の値を返す(Java モック)

これの JMockito 版です。
blueskyarea.hatenablog.com

Mockito 版の記事と同じクラスをテスト対象にしました。

テストコード(JUnit)
import mockit.Expectations;
import mockit.Mocked;
import mockit.integration.junit4.JMockit;

@RunWith(JMockit.class)
public class MemberTest {	
	// Without mock
	@Test
	public void testGetMemberInfoWithoutMock() {
		Member member = new Member(1, "hoge");
		assertThat(member.getPointCard().getPoint(), is(1000L));
	}
	
	// With Mock
	@Test
	public void testGetMemberInfoWithMock(@Mocked Member member) {
	    PointCard pointCard = new PointCard(1, "hoge", 2000L);
	    new Expectations() {
            {
                member.getPointCard();
                result = pointCard;
            }
        };
            assertThat(member.getPointCard(), is(pointCard));
	    assertThat(pointCard.getPoint(), is(2000L));
	}
}

@RunWith(JMockit.class) アノテーションを付けないと、エラーで怒られました。

java.lang.IllegalStateException: JMockit wasn't properly initialized; check that jmockit.jar precedes junit.jar in the classpath (if using JUnit; if not, check the documentation)

classpath 内において、jmockit.jar を junit.jar よりも先に定義しておく必要があるみたいです。
自分の環境下で上手く出来なかったため、代わりにこのアノテーションで回避してます。

結果

Mockito と同様のモックを生成することが出来ました。
Mockito では期待する動作を when().thenReturn() で定義していましたが、JMockito では Expectations で定義します。
この程度の内容であれば Mockito とどちらが使いやすいか検討するまでの違いは見当たりません。

Mockito と JMockito 名前は非常に似ていますが、モックの書き方は全く異なるので、プロジェクト単位でどちらかを使うかは決めた方が良さそうです。
「モックの作り方」についてネットの検索結果をそのまま適用していると、いつの間にか混在しているみたいなこともありそうです。

python bash のコマンドを実行

調べてみると色んなやり方が見つかりましたが、自分にとって一番シンプルだったやり方を。

import subprocess

bashCommand = "ls -alt"
output = subprocess.Popen(bashCommand, stdout=subprocess.PIPE, shell=True).communicate()[0]
print(output)
コマンドの意味とか

subprocess.Popen
新しいプロセスで子のプログラムを実行してくれる。

stdout=subprocess.PIPE
標準出力をパイプする。コンソールに表示させない。
communicate() はタプル (stdoutdata, stderrdata) を返す。
戻り値のタプルから None ではない値を取得するためには、 stdout=PIPE または stderr=PIPE を指定しなければならない。

shell=True
shell が True なら、指定されたコマンドはシェルによって実行される。

Popen.communicate(input)
プロセスと通信する。
end-of-file に到達するまでデータを stdin に送信し、stdout および stderr からデータを受信する。
オプション引数 input には子プロセスに送られる文字列か、あるいはデータを送らない場合は None を指定する。
受信したデータはメモリにバッファされるため、返されるデータが大きい場合はこのメソッドを使うべきではない。

結果
$ ls -alt
合計 12
drwxrwxr-x 2 xx xx 4096  6月 25 23:18 .
-rw-rw-r-- 1 xx xx  339  6月 25 23:18 execBashCommand.py
drwxrwxr-x 9 xx xx 4096  6月 25 22:40 ..
$ python execBashCommand.py 
合計 12
drwxrwxr-x 2 xx xx 4096  6月 25 23:22 .
-rw-rw-r-- 1 xx xx  309  6月 25 23:22 execBashCommand.py
drwxrwxr-x 9 xx xx 4096  6月 25 22:40 ..

普通に bash で実行したコマンドと同じ結果が得られた。
今回、stdout が欲しいから、communicate()[0] としている。
もし、communicate()[1] とした場合

$ python execBashCommand.py 
None

stderr の値が(実際には存在しないから None)が出力されている。