きり丸の技術日記

技術検証したり、資格等をここに残していきます。

Pythonで関数とNoneの型ヒントをつけたい(Resolve TypeError: unsupported operand type(s) for |: 'function' and 'NoneType')

Pythonで関数とNoneが取りうるパラメータに対して、Union型で型ヒントを与えようとしたところエラーになったので解決方法をメモします。

環境

  • Python
    • 3.9

対応

Union型を使わずに、Optional型でヒントを与えます。今回ハマっていたのは、sqlalchemyand_条件をパラメータとして渡したかったので、そのユースケースを記載します。

from typing import Callable, Literal, Optional, Union

from sqlalchemy.sql.elements import ColumnElement
from sqlalchemy.sql._typing import _ColumnExpressionArgument

AndCallable = Callable[[Union[Literal[True], _ColumnExpressionArgument[bool]], _ColumnExpressionArgument[bool]], ColumnElement[bool]]

def test(
    OK: Optional[AndCallable],
    OK2: Union[AndCallable, None],
    OK3: AndCallable | None,
):
    pass

ソースコード

終わりに

Union型で型ヒントを定義できるのは、Python3.9以降だったので頑張ってUnion型で定義しようとしてしまいました。

一応、ヘルプではこう表現しているので、やろうと思ったら行けると思うんですけど。

Optional[X] is equivalent to X | None (or Union[X, None]).

あまりないケースかもしれませんが、残しておきます。

参考情報

旧対応

※ コメントを貰う前の対応を残しておきます。コンパイルや実行処理自体は通るんですけどね…。

def test(
  OK: Optional[and_],
  NG: and_ | None
):
  pass

NG側の型ヒントだと、次のようなエラーが発生してしまいます。

E   TypeError: unsupported operand type(s) for |: 'function' and 'NoneType'

Git LFSでプロキシを経由させる・させない(HTTPS_PROXY, NO_PROXY)

Git Large File System(Git LFS)のツールを使っていて、プロキシを経由させたり、経由させないようにする方法が見つからなかったのでメモします。

環境

  • Mac
    • zsh

対応

次の設定値を使用します。大文字と小文字で設定値の優先度が異なります。

# 上に記載しているほど優先度が高いです

# プロキシサーバの設定
HTTPS_PROXY
https_proxy
HTTP_PROXY
http_proxy

# プロキシを経由させない設定
NO_PROXY
no_proxy

その設定や優先度はどこから読み取れるか、というのは次のソースコードに載っています。

終わりに

実環境ではNO_PROXYno_proxyを違う値を設定していたので、全然疎通が取れずに困っていました。gitコマンドは疎通できるが、git lfsが疎通できない理由がわからずにハマってしまっていました。

しかも、README.mdproxyの設定が記載されておらずにINSTALLING.mdだけにしか記載がなく、NO_PROXYに関してはどこにも書いていないのが非常に困りました。「ドキュメントに反映して欲しい!」ってIssueは上げたのですが、Gitコマンドと同じ挙動をさせているというコメントを貰ったので、特に反論もせずにCloseしました。

参考情報

SpringのValueに初期値を与える(エラーを起こさずにnullも初期値にする)

小ネタ。@Valueで初期値を与えたい時の記法をメモします。マッピングするプロパティがない場合、BeanCreationExceptionが発生してしまうので、それが発生しないようにします。

環境

  • Java
    • 17
  • SpringBootTest
    • 2.7.4

ゴール

  • SpringのValueで初期値を渡す
  • マッピング先がない場合にExceptionではなくnullを設定する
# 次のExceptionが発生しないようにする
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'kirimaru.config.SpringValueTests': Injection of autowired dependencies failed; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'app.config.appName2' in value "${app.config.appName2}"

対応

@Value(${placeholder:defaultValue})と記載する。

app.config.appNameにアプリケーション名を設定していた場合、@Value("${app.config.appName:defaultName}")と記載すると設定値を表示します。設定されていない場合はデフォルト値のdefaultNameを表示します。

nullを初期値に渡したい場合は、@Value("${placeholder:#{null}}")として#{null}を渡す必要があります。もし#{}を使用しない場合はnullという文字列が渡ってしまいます。

テスト

次のテストコードで動作確認しました。今回はDIしていることだけを確認したいため、例外が発生することを確認したい場合はSpringBootTestを使用してください。

@SpringJUnitConfig(SpringValueTests.Config.class)
@TestPropertySource(properties = {"app.config.appName=testApplication"})
class SpringValueTests {

  @Value("${app.config.appName:defaultName}")
  private String appName;
  @Value("${app.config.appName2:defaultName}")
  private String appName2;
  @Value("${app.config.appName3:null}")
  private String appName3;
  @Value("${app.config.appName4:#{null}}")
  private String appName4;

  @ComponentScan({"kirimaru.biz.domain.hogehoge"})
  public static class Config {}

  @Test
  void test_01() {
    SoftAssertions softly = new SoftAssertions();

    softly.assertThat(appName).isEqualTo("testApplication");
    softly.assertThat(appName2).isEqualTo("defaultName");
    softly.assertThat(appName3).isEqualTo("null");
    softly.assertThat(appName4).isNull();

    softly.assertAll();
  }
}

ソースコード

終わりに

開発環境ではproxyを経由したいが、本番環境ではproxyを経由したくない。しかし、変な設定値を設定ファイルに残したくない。

そのようなユースケースで使用しました。地味にやり方が分からなかったので、まとめられてよかったです。

類似情報