技術と日常。

日々の気が付いたこと・気になったことを残しておきます。

[PyCharm]Optimize importsで必要なimportが消えてしまう時に読むページ

概要

PycharmやIntelliJなどのJetBrainsのIDEでは、2021.2から、保存時のアクションで、オートフォーマットができるようになりました

pleiades.io

それまでは別のプラグインが必要だったので、大変助かりますね

さて、その中のOptimize importsに関して
例えばJavaであれば使用されていないimportがなくなることは困らないのですが、Pythonの場合は、言語の特性上、importだけで使用・適用を行う場面が存在します。
すると、Optimize importsは、ソースコード内で使用されないimportを自動で消してしまいます。

この対策について、本記事を残しておきます。

Optimize importsから除外する対策

一つは、以下のように、対象のimportの前に# noinspection PyUnresolvedReferencesを付与します

# noinspection PyUnresolvedReferences
import os

これでOptimize importをかけても、そのimportは守られます。

Optimize importsを無効にする対策

そもそも設定から無効にするには、
冒頭で挙げたActions on Save派の方は、Tools > Actions on Saveよりチェックを外すことで
Ctrl+Alt+L(Mac: Command+Option+L)の手動フォーマット派の方は、Ctrl+Alt+Shift+L(Mac: Command+Option+Shift+L)から表示されるダイアログより、外すことができます

参考

python - Make IDE/linter ignore a seemingly unused import - Stack Overflow

コードの再フォーマットと再配置 | PyCharm ドキュメント

[Git].gitignoreで無視したディレクトリの中のサブディレクトリを追跡する

.gitignoreで無視したディレクトリの中のサブディレクトリを追跡する

以下のようなファイル構造の時に、directoryを無視しつつ、subdirectoryだけを追跡したいと考えます。

│  .gitignore
│
└─directory
    │  not-important.txt
    │
    └─subdirectory
            important.txt

.gitignoreに以下のように書くと、うまくいきません。
これは、 ディレクトリを無視する構文(/directory/)を使用すると、それ以下のファイルは常に無視をされる ためです

/directory/
!/directory/subdirectory

そのため、 ディレクトリの無視ではなく、ファイルのワイルドカードでの無視 を使用します。
ファイル単位で無視 -> ディレクトリを追跡 という手順を踏むと、うまくいきます。

# ファイル単位で無視
/directory/*

# ディレクトリを追跡
!/directory/subdirectory/

git add ., git statusを叩くと、追加されていることが確認できます

>git add .
>git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   .gitignore
        new file:   directory/subdirectory/important.txt

さらに前の行にて無視されているとき

さらに前の行にて/directoryが無視されているときは、ファイル単位で無視 -> ディレクトリを追跡 を繰り返し行います

# ファイル単位で無視
/*

# ディレクトリを追跡
!/directory/

# ファイル単位で無視
/directory/*

# ディレクトリを追跡
!/directory/subdirectory/

参考

git - .gitignore exclude folder but include specific subfolder - Stack Overflow

[Docker]使い捨てのMySQLをサクっと作る方法

Dockerにて使い捨てのMySQLをサクっと作る方法

次のコマンドにて、サクっと作って検証できます。

コンテナ起動

>docker run --rm --name mysql -e MYSQL_DATABASE=mydb -e MYSQL_ROOT_PASSWORD=root-password -d mysql:latest

接続

>docker exec -it mysql /usr/bin/mysql -uroot -proot-password mydb

コンテナ終了

>docker stop mysql

参考

DockerコンテナでMySQLを動かす方法 | ばったんの技術系ブログ

[SQL]WHERE ~ IN句を複数条件(タプル)にて指定する方法は?

WHERE ~ IN句を複数条件(タプル)にて指定する方法

以下のテーブルを考えます

mysql> CREATE TABLE fruits (
    ->     category_1 int,
    ->     category_2 int,
    ->     fruit varchar(10)
    -> );
Query OK, 0 rows affected (0.06 sec)

mysql> INSERT INTO fruits VALUES
    -> (1, 1, "apple"),
    -> (1, 2, "banana"),
    -> (2, 1, "kiwi"),
    -> (2, 2, "orange");
Query OK, 4 rows affected (0.01 sec)

mysql> SELECT * FROM fruits;
+------------+------------+--------+
| category_1 | category_2 | fruit  |
+------------+------------+--------+
|          1 |          1 | apple  |
|          1 |          2 | banana |
|          2 |          1 | kiwi   |
|          2 |          2 | orange |
+------------+------------+--------+
4 rows in set (0.00 sec)

ここで、(category_1, category_2)(1, 1)(2, 2)のものを取得したいと考えます。

ANDとORを使用しても書けるのですが……、

SELECT fruit FROM fruits
WHERE (category_1 = 1 AND category_2 = 1)
OR (category_1 = 2 AND category_2 = 2);

以下のようにINを使うことで、スマートに書くことが可能です。

SELECT fruit FROM fruits
WHERE (category_1, category_2) IN ((1, 1), (2, 2));

PostgreSQL, DB2に関しては、VALUESが必要のようです

SELECT fruit FROM fruits
WHERE (category_1, category_2) IN (VALUES (1, 1), (2, 2));

参考

mysql - selecting where two columns are in a set - Database Administrators Stack Exchange

[Gradle]タスクの依存関係を表示する

きっかけ

Mavenではビルドライフサイクルの中で、決められた一連のタスクが頭から流れるため、なんとなく覚えていれば「この操作の前に処理を挟みたければあのタスクだな。」等がぱっとわかると思います。
Gradleだと、タスクの自由度の高さがある一方「このタスクってどういう順で実行されるんだけ?そもそもなんでこの順になるんだっけ?」となることがあります(筆者はなりました)。
そこで、Gradleでタスクの依存関係を確認する方法を調査しました。

タスクが実行される順序を確認する

gradle <タスク名> --dry-runを使用することで、どの順で何のタスクが実行されるかを表示することができます。

>./gradlew build --dry-run  
:compileJava SKIPPED
:processResources SKIPPED
:classes SKIPPED
:jar SKIPPED
:startScripts SKIPPED
:distTar SKIPPED
:distZip SKIPPED
:assemble SKIPPED
:compileTestJava SKIPPED
:processTestResources SKIPPED
:testClasses SKIPPED
:test SKIPPED
:check SKIPPED
:build SKIPPED

BUILD SUCCESSFUL in 1s

タスクの依存関係を確認する

gradle-taskinfoプラグインを使用します

gitlab.com

build.gradleに、以下のように追加します

plugins {
    id 'org.barfuin.gradle.taskinfo' version '2.0.0'
}

gradle tiTree <タスク名>にて、treeが表示されます。

>./gradlew tiTree assemble

> Task :tiTree
:assemble                                  (org.gradle.api.DefaultTask)
+--- :distTar                              (org.gradle.api.tasks.bundling.Tar)
|    +--- :jar                             (org.gradle.api.tasks.bundling.Jar)
|    |    `--- :classes                    (org.gradle.api.DefaultTask)
|    |         +--- :compileJava           (org.gradle.api.tasks.compile.JavaCompile)
|    |         `--- :processResources      (org.gradle.language.jvm.tasks.ProcessResources)
|    `--- :startScripts                    (org.gradle.api.tasks.application.CreateStartScripts)
|         `--- :jar                        (org.gradle.api.tasks.bundling.Jar)
|              `--- :classes               (org.gradle.api.DefaultTask)
|                   +--- :compileJava      (org.gradle.api.tasks.compile.JavaCompile)
|                   `--- :processResources (org.gradle.language.jvm.tasks.ProcessResources)
+--- :distZip                              (org.gradle.api.tasks.bundling.Zip)
|    +--- :jar                             (org.gradle.api.tasks.bundling.Jar)
|    |    `--- :classes                    (org.gradle.api.DefaultTask)
|    |         +--- :compileJava           (org.gradle.api.tasks.compile.JavaCompile)
|    |         `--- :processResources      (org.gradle.language.jvm.tasks.ProcessResources)
|    `--- :startScripts                    (org.gradle.api.tasks.application.CreateStartScripts)
|         `--- :jar                        (org.gradle.api.tasks.bundling.Jar)
|              `--- :classes               (org.gradle.api.DefaultTask)
|                   +--- :compileJava      (org.gradle.api.tasks.compile.JavaCompile)
|                   `--- :processResources (org.gradle.language.jvm.tasks.ProcessResources)
`--- :jar                                  (org.gradle.api.tasks.bundling.Jar)
     `--- :classes                         (org.gradle.api.DefaultTask)
          +--- :compileJava                (org.gradle.api.tasks.compile.JavaCompile)
          `--- :processResources           (org.gradle.language.jvm.tasks.ProcessResources)


BUILD SUCCESSFUL in 1s
1 actionable task: 1 executed

gradle tiOrder <タスク名>にて、タスクの順番も表示可能です。

>./gradlew tiOrder assemble   

> Task :tiOrder

In order to execute [:assemble], the following tasks would be executed in this order:

  1. :compileJava      (org.gradle.api.tasks.compile.JavaCompile)
  2. :processResources (org.gradle.language.jvm.tasks.ProcessResources)
  3. :classes          (org.gradle.api.DefaultTask)
  4. :jar              (org.gradle.api.tasks.bundling.Jar)
  5. :startScripts     (org.gradle.api.tasks.application.CreateStartScripts)
  6. :distTar          (org.gradle.api.tasks.bundling.Tar)
  7. :distZip          (org.gradle.api.tasks.bundling.Zip)
  8. :assemble         (org.gradle.api.DefaultTask)


BUILD SUCCESSFUL in 1s
1 actionable task: 1 executed

詳しくはリンクより、ドキュメントをご確認ください。

出典

Is there a way to list task dependencies in Gradle? - Stack Overflow

[中野で時計買取]本当に高く売れるのか調査してみた

背景

以前インターネットや雑誌等に影響されて、一般的に「高級時計」される時計を購入しました。
しかし、もともとインドアなのに加えて、感染症の影響により外に出る機会ががくんと減り、また、時計に関する思いも、実際に使ってみたり、在宅の日々を過ごしたりする中で変化したので、思い切って買取に出すことにしました。
一括査定サイトや、フリマアプリなど、いろいろな方法を検討したのですが、今回は時計で有名な中野のお店を巡ってみることにしました。

事前にどこに持っていこうか情報収集をしようと「中野 時計買取」で検索すると、いろいろなお店・口コミが出てくるのですが、「ここは凄くよく書いてあるけどステルスマーケティングでは……?」とか「このお店は、実在庫がないとか買い叩かれたとか、結構悪いことも書かれているな……。」とか、いろいろ読んでいるうちに、不安になってきました。
そこで、実際にいろいろなお店を回って買取査定をした、その備忘録として、本記事を残しておきます。

査定に出したもの・姿勢

詳細な型番は控えますが、スイスの時計メーカーIWC (International Watch Company)の、以下2本になります。

  • マークシリーズ(パイロットウォッチ)
  • アクアタイマー(ダイバーズウォッチ)

両方とも、箱・保証書がついた状態で、インターネットで中古にて購入しました。
査定員の方から「綺麗ですし精度もいいです。実際に着用されていましたか?」と聞かれる程度には、良い状態の物でした(着用時は気を遣ったり、使い終わったら拭いたりと、大事に使っていました)。

時計を売る温度感を聞かれた際には「本日は冷やかしではなく、一通り回らせて頂いたうえで、一番高いところに時計を置いて帰る。」ということをちゃんと伝えました。
また、回るにつれて、それ以前に回ったお店より安い金額を提示された場合には「他店ではこれくらいの金額を提示されている。」ということを伝えながら回りました。

査定結果

以下査定が出た順となります(単位: 万円/四捨五入あり)。

お店 マークシリーズ アクアタイマー 備考
A 23 26 「マークシリーズはトップガンシリーズの影響等で人気で、割といい値段と思う。」
「アクアタイマーはうちの店だとちょっと売れ行きが良くなくてこのお値段です。」とのこと
B 22 27
C 24 29
D 26 24 唯一マークシリーズの方が高かったお店。話を聞くと、お店によって得意・不得意な商品があるとのこと
マークシリーズはこちらで買取を依頼
E 23.1 25.2
F 23 30
G 23 -> 25 29 -> 30 -> 30.5 査定金額が出てから、今までの結果を伝えると、それぞれ25万円、30万円にアップした
アクアタイマーはこちらで買取を依頼。
最後に戻ってきて買取に出すときに、30.5万円になりませんか?と聞いて、OKをもらった
H 21 23.6

まとめると、以下のようになりました。

時計 最高金額 最低金額 金額差
マークシリーズ 26 21 5
アクアタイマー 30.5 23.6 6.9
合計 56.5 44.6 11.9

感想

まず、最高金額に関しては、インターネットの中古相場等から考えて、予想を超える結果となりました。率直に「高い。結構満足したな。」という気持ちです。

お店ごとの金額の関しては、いろいろ回りながら査定について話を聞いた結果、商品の相場を前提に、お店の得意・不得意により、どうしても差は出るものなのだなと感じました。

合計の最高・最低金額は、金額で言うと20%以上の差がつきました。
時間と体力に余裕があるなら、いろいろ回ってみるのが良いかと思います。

高く売るコツと豆知識

希望金額を聞かれても答えない

査定金額を出す前に「本日はいくらくらいなら置いていって頂けますか?」「他店さんはいくらくらいつけてますか?」と聞かれることがありましたが「まずは是非お値段を出してください。」と、答えない方針にしました。
答えてしまうと、その金額をベースに値付けをされることがあるようです。

最後にもうちょっと上乗せできないか聞いてみる

いろいろ回って、最終的にここに出すぞ!と決め、持って行った段階で「もうちょっと上がりませんか?」と聞いてみるのはどうでしょう。
今回は、マークシリーズの方は電卓を3分くらい叩いたのちに「すみません、限界でした。」とお断りに。
アクアタイマーも同じように聞いたら、すぐに「いろいろ回ったうえで、折角戻ってきてくれたので、OKですよ!」と快諾頂けました。
最後のチャレンジ、ぜひ迷惑にならない程度にしてみてください。

時計1本あたり、査定に10分~15分は見ておく

今回は8店舗回りましたが、時間で言うと4時間ほどかかりました。
私も2店目からは「1店目で動作と精度は問題ないと言われているので、機能は問題ない前提で、外見ベースで値段を出してくれないか。」とお願いしてみたりしましたが、それでもその位かかりました。
時間に余裕を持たせて行くのをお勧めします。

最後に

本記事が、時計を愛する皆様のお役に立てば幸いです。

[Java]Streamを閉じないとリソースリークするよ!っていう話(AutoClosable)

本記事は、JJUGにて登壇したものを、記事向けに加筆修正したものです。
その際の動画・スライドはこちらのページでご覧頂けます。 beppy.hatenablog.com

StreamのAPI仕様確認

StreamのAPI仕様を確認すると、AutoClosableがついています

AutoClosableがついているということは、以下のように、try-with-resources文にて、リソースとして宣言ができます。
(このケースでは、こう書き換えても何もメリットはないのですが……。)

Stream.of(1, 2, 3).forEach(System.out::println);
↓
try (Stream<Integer> stream = Stream.of(1, 2, 3)) {
    stream.forEach(System.out::println);
}

> Task :run
1
2
3

AutoClosableが必要となるケース

ではなぜこれが用意されているのかというと、 Readerや、DBのConnection等のリソースを、Stream内部で使用している場合に、使い終わった時にcloseができるようにするため です。
怠ると、リソースやコネクションのリークにつながる可能性があります。

java.nio.file.Files

list

public static Stream list(Path dir) throws IOException

ディレクトリ内のエントリを要素に持つ遅延設定Streamを返します。 リストは再帰的ではありません。
このメソッドは、 try-with-resources文または類似の制御構造内で使用して、ストリーム操作が完了した後にストリームのオープン・ディレクトリがすぐに閉じられるようにする必要があります。

lines

public static Stream lines(Path path, Charset cs) throws IOException

ファイル内のすべての行をStreamとして読み取ります。
返されるストリームは、Readerをカプセル化します。 ファイル・システム・リソースのタイムリな破棄が必要な場合は、try-with-resources構文を使用して、ストリーム操作の完了後にストリームのcloseメソッドが呼び出されるようにしてください。

jOOQ(ORM)

ResultQuery

Stream stream() throws DataAccessException

……
Clients should ensure the Stream is properly closed, e.g. in a try-with-resources statement:

try (Stream stream = query.stream()) {
// Do things with stream
}

close処理を付与したいなら

上記のようなtry-with-resources文で閉じてくれるStreamを自作したい場合、onClose()を使用することで作成することができます。

S onClose(Runnable closeHandler)

追加のクローズ・ハンドラを含む同等のストリームを返します。
クローズ・ハンドラはストリーム上でclose()メソッドが呼び出されたときに実行されますが、ハンドラの実行順序は追加された順番になります。
いずれかのクローズ・ハンドラから例外がスローされても、すべてのクローズ・ハンドラが実行されます。
……
これは中間操作です。

例えば、以下のようなコードを実行すると、try-with-resource文を抜けたときに、追加された順番でハンドラが実行されていることが確認できます。

Runnable first = () -> System.out.println("first called");
Runnable second = () -> System.out.println("second called");
try (Stream<Integer> stream
        = Stream.of(1, 2, 3).onClose(first).onClose(second)) {
    stream.forEach(System.out::println);
}

> Task :run
1
2
3
first called
second called

もっと詳しく知りたい場合、前述したFiles.lines()の実装が参考になりそうなので、併せてご確認下さい

最後に

恥ずかしながら、私はStreamが閉じれることを知らず、特にjOOQのは見逃す寸前で、危うくコネクションリークを起こすところでした。
改めて、ドキュメントやJavaDocはちゃんと読んで、理解してから使う必要があるなと、反省しました。

それでももし見落としたとしても、本知識があれば「このStreamは外部のリソースを使ってそうだけど大丈夫かな……?」と、注意するきっかけとなると思います。
本記事が、皆様のよいJava Lifeの助けになれば幸いです。

参考・出典

Files (Java SE 17 & JDK 17)

ResultQuery (jOOQ 3.17.4 API)

BaseStream (Java SE 17 & JDK 17)

Java8 Stream APIの基本(8) - 終端操作3(close) - エンタープライズギークス (Enterprise Geeks)