otsuvalidator 1.1.0.312__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2021 Otsuhachi
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,118 @@
1
+ Metadata-Version: 2.4
2
+ Name: otsuvalidator
3
+ Version: 1.1.0.312
4
+ Summary: A useful python package named otsuvalidator.
5
+ Home-page: https://github.com/Otsuhachi/OtsuValidator
6
+ Author: Otsuhachi
7
+ Author-email: Otsuhachi <agequodagis.tufuiegoeris@gmail.com>
8
+ License: MIT License
9
+ Classifier: Programming Language :: Python :: 3.12
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Operating System :: OS Independent
12
+ Requires-Python: >=3.12
13
+ Description-Content-Type: text/markdown
14
+ License-File: LICENSE.txt
15
+ Dynamic: author
16
+ Dynamic: home-page
17
+ Dynamic: license-file
18
+ Dynamic: requires-python
19
+
20
+ - [概要](#概要)
21
+ - [インストール](#インストール)
22
+ - [モジュール](#モジュール)
23
+ - [basesモジュールのクラス](#basesモジュールのクラス)
24
+ - [validatorsモジュールのクラス](#validatorsモジュールのクラス)
25
+ - [convertersモジュールのクラス](#convertersモジュールのクラス)
26
+ - [継承規則](#継承規則)
27
+ - [Validator継承規則](#validator継承規則)
28
+ - [VContainer継承規則](#vcontainer継承規則)
29
+ - [Converter継承規則](#converter継承規則)
30
+
31
+ ---
32
+ # 概要
33
+
34
+ このライブラリは、値の正常性を検証するバリデータ群と、検証前に型変換を試みるコンバータ群を提供するユーティリティです。
35
+ ディスクリプタとしてクラス属性に使用することで不適切な値の代入を防止できるほか、単独の関数・オブジェクトとして値を検証・変換することも可能です。
36
+
37
+ ---
38
+ ## インストール
39
+
40
+ **インストール**
41
+ ```bash
42
+ pip install otsuvalidator
43
+ ```
44
+
45
+ **アップデート**
46
+ ```bash
47
+ pip install -U otsuvalidator
48
+ ```
49
+
50
+ **アンインストール**
51
+ ```bash
52
+ pip uninstall otsuvalidator
53
+ ```
54
+
55
+ ## モジュール
56
+ 本ライブラリは主に以下の3つのモジュールで構成されています。
57
+
58
+ モジュール名|概要
59
+ :--:|:--:
60
+ `bases`|バリデータ・コンバータの基底クラスを定義。自作のバリデータを作成する際に使用します。
61
+ `validators`|値の検証のみを行うバリデータ群が定義されています。
62
+ `converters`|型変換を試みた上で検証を行うコンバータ群が定義されています。
63
+
64
+
65
+ ### basesモジュールのクラス
66
+ - `Validator`: すべてのバリデータおよびコンバータの基底クラス。
67
+ - `VContainer`: コンテナ(リストや辞書など)用バリデータの基底クラス。要素が可変なオブジェクトを検証する際に使用します。
68
+ - `Converter`: コンバータの基底クラス。
69
+ - `VChain`: バリデータ・コンバータの連結をサポートします。
70
+
71
+ ### validatorsモジュールのクラス
72
+ ※破線されたクラスは非推奨です。
73
+ クラス|概要|期待する型
74
+ :--:|:--:|:--:
75
+ VBool|真偽値オブジェクトか検証|bool
76
+ VChoice|規定の選択肢の中から1つが選択されているか検証|Any
77
+ VNumerical / ~~VNumber~~|適切な数値か検証|int / float
78
+ VFloat|適切な浮動小数点数か検証|float
79
+ VInt|適切な整数か検証|int
80
+ VPath|適切なパスか検証|pathlib.Path
81
+ VString|適切な文字列か検証|str
82
+ ~~VRegex~~|適切な正規表現か検証。`VString(pattern=...)`に合併。|str
83
+ VDict|適切な構造の辞書か検証|dict
84
+ VList|適切なリストか検証|list
85
+ VTuple|適切なタプルか検証|tuple
86
+ VTimedelta|適切なtimedelta型か検証|datetime.timedelta
87
+
88
+ ### convertersモジュールのクラス
89
+ ※破線されたクラスは非推奨です。
90
+ クラス|概要
91
+ :--:|:--:
92
+ CBool|Yes / Noとして解釈できる値に対し`bool`型への変換を試みて検証
93
+ CNumerical / ~~CNumber~~|`int` / `float`型への自動変換を試みて検証
94
+ CFloat|`float`型への自動変換を試みて検証
95
+ CInt|`int`型への自動変換を試みて検証
96
+ CPath|`pathlib.Path`型への自動変換を試みて検証
97
+ CString|`str`型への返還を試みて検証
98
+ CTimedelta|`datetime.timedelta`型への自動変換を試みて検証
99
+
100
+ ## 継承規則
101
+ 自作のバリデータやコンバータを正しく定義するための規則です。
102
+
103
+ ### Validator継承規則
104
+ 1. 命名: クラス名は`V{検証したいクラス名}`とします(例: `VString`)。
105
+ 2. 継承: `Validator`クラスを継承します。
106
+ 3. 定義: `validate`メソッドを定義し、検証が成功した場合は受け取った`value`を返します。
107
+ 4. 変換: `validate`内で`value`の型変換を行ってはいけません(変換を行う場合は`Converter`を使用)。
108
+
109
+ ### VContainer継承規則
110
+ 1. 命名: クラス名は`V{検証したいクラス名}`とします(例: `VList`)。
111
+ 2. 継承: `VContainer`クラスを継承します。
112
+ 3. 定義: `validate`メソッドを定義し、検証通過時は`value`を返します。中身の各要素への検証規則を設定します。
113
+ 4. 変換: コンテナ自体の型変換は行いません。ただし要素に対してはオプション設定に応じます。
114
+
115
+ ### Converter継承規則
116
+ 1. 命名: `C{変換検証したいクラス名}`とします(例: `CInt`)。
117
+ 2. 継承: `(対象クラスのバリデータ, Converter)`の順で多重継承します。
118
+ 3. 定義: `validate`内で型変換を行い、`return super().validate(value)`で検証して返します。
@@ -0,0 +1,99 @@
1
+ - [概要](#概要)
2
+ - [インストール](#インストール)
3
+ - [モジュール](#モジュール)
4
+ - [basesモジュールのクラス](#basesモジュールのクラス)
5
+ - [validatorsモジュールのクラス](#validatorsモジュールのクラス)
6
+ - [convertersモジュールのクラス](#convertersモジュールのクラス)
7
+ - [継承規則](#継承規則)
8
+ - [Validator継承規則](#validator継承規則)
9
+ - [VContainer継承規則](#vcontainer継承規則)
10
+ - [Converter継承規則](#converter継承規則)
11
+
12
+ ---
13
+ # 概要
14
+
15
+ このライブラリは、値の正常性を検証するバリデータ群と、検証前に型変換を試みるコンバータ群を提供するユーティリティです。
16
+ ディスクリプタとしてクラス属性に使用することで不適切な値の代入を防止できるほか、単独の関数・オブジェクトとして値を検証・変換することも可能です。
17
+
18
+ ---
19
+ ## インストール
20
+
21
+ **インストール**
22
+ ```bash
23
+ pip install otsuvalidator
24
+ ```
25
+
26
+ **アップデート**
27
+ ```bash
28
+ pip install -U otsuvalidator
29
+ ```
30
+
31
+ **アンインストール**
32
+ ```bash
33
+ pip uninstall otsuvalidator
34
+ ```
35
+
36
+ ## モジュール
37
+ 本ライブラリは主に以下の3つのモジュールで構成されています。
38
+
39
+ モジュール名|概要
40
+ :--:|:--:
41
+ `bases`|バリデータ・コンバータの基底クラスを定義。自作のバリデータを作成する際に使用します。
42
+ `validators`|値の検証のみを行うバリデータ群が定義されています。
43
+ `converters`|型変換を試みた上で検証を行うコンバータ群が定義されています。
44
+
45
+
46
+ ### basesモジュールのクラス
47
+ - `Validator`: すべてのバリデータおよびコンバータの基底クラス。
48
+ - `VContainer`: コンテナ(リストや辞書など)用バリデータの基底クラス。要素が可変なオブジェクトを検証する際に使用します。
49
+ - `Converter`: コンバータの基底クラス。
50
+ - `VChain`: バリデータ・コンバータの連結をサポートします。
51
+
52
+ ### validatorsモジュールのクラス
53
+ ※破線されたクラスは非推奨です。
54
+ クラス|概要|期待する型
55
+ :--:|:--:|:--:
56
+ VBool|真偽値オブジェクトか検証|bool
57
+ VChoice|規定の選択肢の中から1つが選択されているか検証|Any
58
+ VNumerical / ~~VNumber~~|適切な数値か検証|int / float
59
+ VFloat|適切な浮動小数点数か検証|float
60
+ VInt|適切な整数か検証|int
61
+ VPath|適切なパスか検証|pathlib.Path
62
+ VString|適切な文字列か検証|str
63
+ ~~VRegex~~|適切な正規表現か検証。`VString(pattern=...)`に合併。|str
64
+ VDict|適切な構造の辞書か検証|dict
65
+ VList|適切なリストか検証|list
66
+ VTuple|適切なタプルか検証|tuple
67
+ VTimedelta|適切なtimedelta型か検証|datetime.timedelta
68
+
69
+ ### convertersモジュールのクラス
70
+ ※破線されたクラスは非推奨です。
71
+ クラス|概要
72
+ :--:|:--:
73
+ CBool|Yes / Noとして解釈できる値に対し`bool`型への変換を試みて検証
74
+ CNumerical / ~~CNumber~~|`int` / `float`型への自動変換を試みて検証
75
+ CFloat|`float`型への自動変換を試みて検証
76
+ CInt|`int`型への自動変換を試みて検証
77
+ CPath|`pathlib.Path`型への自動変換を試みて検証
78
+ CString|`str`型への返還を試みて検証
79
+ CTimedelta|`datetime.timedelta`型への自動変換を試みて検証
80
+
81
+ ## 継承規則
82
+ 自作のバリデータやコンバータを正しく定義するための規則です。
83
+
84
+ ### Validator継承規則
85
+ 1. 命名: クラス名は`V{検証したいクラス名}`とします(例: `VString`)。
86
+ 2. 継承: `Validator`クラスを継承します。
87
+ 3. 定義: `validate`メソッドを定義し、検証が成功した場合は受け取った`value`を返します。
88
+ 4. 変換: `validate`内で`value`の型変換を行ってはいけません(変換を行う場合は`Converter`を使用)。
89
+
90
+ ### VContainer継承規則
91
+ 1. 命名: クラス名は`V{検証したいクラス名}`とします(例: `VList`)。
92
+ 2. 継承: `VContainer`クラスを継承します。
93
+ 3. 定義: `validate`メソッドを定義し、検証通過時は`value`を返します。中身の各要素への検証規則を設定します。
94
+ 4. 変換: コンテナ自体の型変換は行いません。ただし要素に対してはオプション設定に応じます。
95
+
96
+ ### Converter継承規則
97
+ 1. 命名: `C{変換検証したいクラス名}`とします(例: `CInt`)。
98
+ 2. 継承: `(対象クラスのバリデータ, Converter)`の順で多重継承します。
99
+ 3. 定義: `validate`内で型変換を行い、`return super().validate(value)`で検証して返します。
@@ -0,0 +1,31 @@
1
+ __version__ = "1.1.0.312"
2
+ from .bases import VChain
3
+ from .converters import (
4
+ CBool,
5
+ CFloat,
6
+ CInt,
7
+ CNumber,
8
+ CNumerical,
9
+ CPath,
10
+ CString,
11
+ CTimedelta,
12
+ )
13
+ from .validators import (
14
+ VAny,
15
+ VBool,
16
+ VChoice,
17
+ VDict,
18
+ VFloat,
19
+ VInt,
20
+ VList,
21
+ VNone,
22
+ VNumber,
23
+ VPath,
24
+ VRange,
25
+ VRegex,
26
+ VSequence,
27
+ VString,
28
+ VTimedelta,
29
+ VTuple,
30
+ VType,
31
+ )
@@ -0,0 +1,137 @@
1
+ __all__ = (
2
+ "Converter",
3
+ "Validator",
4
+ "VChain",
5
+ "VContainer",
6
+ )
7
+ from abc import ABC, abstractmethod
8
+ from typing import Any, Literal, Self, overload
9
+
10
+
11
+ class Validator[OutT](ABC):
12
+ def __init__(self) -> None:
13
+ self.public_name: str = ""
14
+ self.private_name: str = ""
15
+
16
+ def __set_name__(self, owner: type[Any], name: str) -> None:
17
+ """クラス定義時に自動的に呼び出される。
18
+
19
+ 例: `age = Validator()`と書くと`name`に"age"が入る。
20
+ """
21
+ self.public_name = name
22
+ self.private_name = f"_{name}"
23
+
24
+ @overload
25
+ def __get__(self, instance: None, owner: type[Any]) -> Self: ...
26
+
27
+ @overload
28
+ def __get__(self, instance: Any, owner: type[Any]) -> OutT: ...
29
+ def __get__(self, instance: Any | None, owner: type[Any]) -> OutT | Self:
30
+ # クラス属性としてアクセスされた場合
31
+ # 例: MyClass.age
32
+ if instance is None:
33
+ return self
34
+ # インスタンス属性としてアクセスされた場合
35
+ # myc_instance.age
36
+ return getattr(instance, self.private_name)
37
+
38
+ def set_name(self, name: str) -> None:
39
+ """バリデータの表示名を動的に設定する。"""
40
+ self.public_name = name
41
+ self.private_name = f"_{name}"
42
+
43
+ @abstractmethod
44
+ def validate(self, value: Any) -> OutT:
45
+ """
46
+ [抽象メソッド] 子クラスで必ず実装する必要がある。
47
+ 値が正しければその値を返し、不正なら例外を投げる。
48
+ """
49
+ raise NotImplementedError()
50
+
51
+ def __set__(self, instance: Any, value: Any) -> None:
52
+ """値を代入する際に呼び出される。
53
+
54
+ `validate`メソッドでチェックを通った値のみ保持する。
55
+ """
56
+ # 1. 値を検証(子クラスの実装を呼び出す。)
57
+ validated_value = self.validate(value)
58
+ # 2. private_nameでインスタンスに保存
59
+ setattr(instance, self.private_name, validated_value)
60
+
61
+ def __or__[NextOutT](self, other: "Validator[NextOutT]") -> "VChain[NextOutT]":
62
+ """パイプ演算子によるバリデータの直列連結をサポートする。"""
63
+ return VChain(self, other)
64
+
65
+
66
+ class Converter[InT, OutT](Validator[OutT]):
67
+ """値を特定の型や形式に変換するディスクリプタの基底クラス。"""
68
+
69
+ @abstractmethod
70
+ def validate(self, value: InT) -> OutT:
71
+ """入力値を変換して返すロジックを実装する。"""
72
+ raise NotImplementedError()
73
+
74
+ def super_validate(self, value: Any) -> OutT:
75
+ """変換を行わず、スーパークラスの検証のみを行う。"""
76
+ return super().validate(value) # type: ignore
77
+
78
+
79
+ class VChain[OutT](Validator[OutT]):
80
+ """複数のバリデータを直列に実行するクラス。"""
81
+
82
+ def __init__(self, *validators: Validator[Any], pass_type: Literal["all", "any"] = "any") -> None:
83
+ super().__init__()
84
+ self.validators: tuple[Validator[Any], ...] = validators
85
+ self.pass_type: Literal["all", "any"] = pass_type
86
+
87
+ def validate(self, value: Any) -> OutT:
88
+ current_value = value
89
+ validators = self.validators
90
+ if self.pass_type == "all":
91
+ for validator in validators:
92
+ current_value = validator.validate(current_value)
93
+ else:
94
+ passed = False
95
+ exceptions = []
96
+ for validator in validators:
97
+ try:
98
+ tmp = validator.validate(current_value)
99
+ current_value = tmp
100
+ passed = True
101
+ except Exception as ex:
102
+ exceptions.append(ex)
103
+ if not passed:
104
+ raise ValueError(*exceptions)
105
+ return current_value
106
+
107
+ def __or__[NextOutT](self, other: Validator[NextOutT]) -> "VChain[NextOutT]":
108
+ return VChain(*self.validators, other)
109
+
110
+
111
+ class VContainer[OutT](Validator[OutT]):
112
+ """
113
+ VDict, VList, VTuple などの構造を持つバリデータの基底クラス。
114
+ スキーマの保持や、動的な再検証フラグの管理を行う。
115
+ """
116
+
117
+ def __init__(
118
+ self,
119
+ schema: Any,
120
+ *,
121
+ monitoring_overwrite: bool = False,
122
+ ) -> None:
123
+ super().__init__()
124
+ self.schema = schema
125
+ self.monitoring_overwrite = monitoring_overwrite
126
+
127
+ def __get__(self, instance: Any | None, owner: type[Any]) -> OutT | Self: # type: ignore
128
+ """
129
+ monitoring_overwrite が True の場合、
130
+ 値を取得する際にも再検証を実行するようにオーバーライド。
131
+ """
132
+ if instance is None:
133
+ return self
134
+ value = getattr(instance, self.private_name)
135
+ if self.monitoring_overwrite:
136
+ return self.validate(value)
137
+ return value
@@ -0,0 +1,282 @@
1
+ __all__ = (
2
+ "CBool",
3
+ "CFloat",
4
+ "CInt",
5
+ "CNumber",
6
+ "CNumerical",
7
+ "CPath",
8
+ "CString",
9
+ "CTimedelta",
10
+ )
11
+
12
+
13
+ import re
14
+ import warnings
15
+ from collections.abc import Callable
16
+ from datetime import timedelta
17
+ from pathlib import Path
18
+ from typing import Any, Literal, override
19
+
20
+ from .bases import Converter
21
+ from .types import FLOAT_INT, StrPath
22
+ from .validators import VBool, VFloat, VInt, VNumber, VPath, VString, VTimedelta
23
+
24
+
25
+ class CBool(VBool, Converter[Any, bool]):
26
+ """VBoolの拡張バリデータ。
27
+
28
+ 一般にYes/Noとして解釈できる値に対し、bool変換を試みます。
29
+ bool(value)とは異なる結果を返すことがあります。
30
+ また、Trueにしたい値、Falseにしたい値をタプル形式で登録することでその通りに判定するようになります。
31
+
32
+ str型, float型, int型に対応しています。
33
+ また、str型はすべてstr.lowerされた後に判定されます。
34
+
35
+ Trueに変換される値
36
+ str型: 'true', 'yes', 'y', '1'
37
+ int型: 1
38
+ float型: 1.0
39
+
40
+ Falseに変換される値
41
+ str型: 'false', 'no', 'n', '0'
42
+ int型: 0
43
+ float型: 0.0
44
+ """
45
+
46
+ def __init__(
47
+ self,
48
+ true_data: tuple[Any, ...] | None = None,
49
+ false_data: tuple[Any, ...] | None = None,
50
+ checker: tuple[Callable[[Any], bool], ...] | None = None,
51
+ ) -> None:
52
+ """ユーザ定義のTrueにしたい値、Falseにしたい値を設定してバリデータを生成します。
53
+
54
+ Args:
55
+ true_data (Optional[Tuple[Any, ...]], optional): Trueにしたい値です。
56
+ false_data (Optional[Tuple[Any, ...]], optional): Falseにしたい値です。
57
+ checker (Optional[Tuple[Callable[[Any], bool], ...]], optional): 真偽判定用の関数です。
58
+ """
59
+ self.true_data = true_data
60
+ self.false_data = false_data
61
+ self.checker = checker
62
+
63
+ def validate(self, value: Any) -> bool:
64
+ # ユーザ定義で真偽確認
65
+ tmp_value = value
66
+ if (checker := self.true_data) is not None and value in checker:
67
+ return super().validate(True)
68
+ if (checker := self.false_data) is not None and value in checker:
69
+ return super().validate(False)
70
+ if (checker := self.checker) is not None:
71
+ for c in checker:
72
+ try:
73
+ tmp = c(tmp_value)
74
+ if tmp is None:
75
+ continue
76
+ tmp_value = tmp # type: ignore
77
+ except Exception:
78
+ continue
79
+ # クラス定義で真偽を確認
80
+ if (tv := type(tmp_value)) is bool:
81
+ pass
82
+ elif tv is str:
83
+ tmp_value: str
84
+ tmp_value = tmp_value.lower()
85
+ if tmp_value in (yes := ("true", "yes", "y", "1")):
86
+ tmp_value = True # type: ignore
87
+ elif tmp_value in (no := ("false", "no", "n", "0")):
88
+ tmp_value = False # type: ignore
89
+ else:
90
+ msg = f"{yes}または{no}のいずれかである必要があります。\n{tmp_value=}"
91
+ raise ValueError(msg)
92
+ elif tv is int:
93
+ if tmp_value == 1:
94
+ tmp_value = True # type: ignore
95
+ elif tmp_value == 0:
96
+ tmp_value = False # type: ignore
97
+ else:
98
+ msg = f"1または0である必要があります。\n{tmp_value=}"
99
+ raise ValueError(msg)
100
+ elif tv is float:
101
+ if tmp_value == 1.0:
102
+ tmp_value = True # type: ignore
103
+ elif tmp_value == 0.0:
104
+ tmp_value = False # type: ignore
105
+ else:
106
+ msg = f"1.0または0.0である必要があります。\n{tmp_value=}"
107
+ raise ValueError(msg)
108
+ else:
109
+ msg = f"Yes/Noとして解釈できる必要があります。\n{tmp_value=}"
110
+ raise TypeError(msg)
111
+ return super().validate(tmp_value)
112
+
113
+
114
+ class CNumerical[OutT: FLOAT_INT](VNumber[OutT], Converter[Any, OutT]):
115
+ """
116
+ 数値を変換し、範囲を検証するコンバータの基底クラス。
117
+ """
118
+
119
+ def __init__(
120
+ self,
121
+ target_type: type[OutT] | None = None,
122
+ min_val: OutT | None = None,
123
+ max_val: OutT | None = None,
124
+ *,
125
+ type_strict: bool = False,
126
+ ) -> None:
127
+ super().__init__(min_val, max_val, type_strict=type_strict)
128
+ self.target_type = target_type
129
+
130
+ @override
131
+ def validate(self, value: Any) -> OutT:
132
+ """入力値を target_type に変換してから、VNumber の検証を実行する。"""
133
+ # 1. 変換試行
134
+ t_type = self.target_type
135
+ try:
136
+ if isinstance(value, bool):
137
+ raise ValueError
138
+ if t_type is None:
139
+ # 動的判定モード
140
+ f_val = float(value)
141
+ if f_val.is_integer():
142
+ converted = int(f_val)
143
+ else:
144
+ converted = f_val
145
+ else:
146
+ converted = t_type(value)
147
+ except (TypeError, ValueError):
148
+ type_name = t_type.__name__ if t_type else "number"
149
+ msg = f"'{self.public_name}'を{type_name}に変換できませんでした: {value!r}。"
150
+ raise ValueError(msg) from None
151
+ return super().validate(converted)
152
+
153
+
154
+ class CNumber[OutT: FLOAT_INT](CNumerical[OutT]):
155
+ """
156
+ [非推奨]CNumberは非推奨です。将来のバージョンで削除されます。
157
+ 代わりにCNumericalを使用してください。
158
+ """
159
+
160
+ def __init__(
161
+ self,
162
+ target_type: type[OutT],
163
+ min_val: OutT | None = None,
164
+ max_val: OutT | None = None,
165
+ *,
166
+ type_strict: bool = False,
167
+ ) -> None:
168
+ msg = "CNumberは非推奨です。代わりにCNumericalを使用してください。"
169
+ warnings.warn(msg, DeprecationWarning, stacklevel=2)
170
+ super().__init__(target_type, min_val, max_val, type_strict=type_strict)
171
+
172
+
173
+ class CInt(CNumerical[int]):
174
+ """
175
+ 値を整数に変換し、範囲を検証するコンバータ。
176
+ """
177
+
178
+ def __init__(
179
+ self,
180
+ min_val: int | None = None,
181
+ max_val: int | None = None,
182
+ ) -> None:
183
+ super().__init__(target_type=int, min_val=min_val, max_val=max_val)
184
+
185
+
186
+ class CFloat(CNumerical[float]):
187
+ """
188
+ 値を浮動小数点数に変換し、範囲を検証するコンバータ。
189
+ """
190
+
191
+ def __init__(
192
+ self,
193
+ min_val: float | None = None,
194
+ max_val: float | None = None,
195
+ ) -> None:
196
+ super().__init__(target_type=float, min_val=min_val, max_val=max_val)
197
+
198
+
199
+ class CPath(VPath, Converter[StrPath, Path]):
200
+ """文字列をPathオブジェクトに変換し、属性を検証するコンバータ。"""
201
+
202
+ def __init__(
203
+ self,
204
+ *extensions: str,
205
+ must_exist: bool = False,
206
+ path_type: Callable[[Path], bool] | None | Literal["dir", "file"] = None,
207
+ ) -> None:
208
+ super().__init__(*extensions, must_exist=must_exist, path_type=path_type)
209
+
210
+ @override
211
+ def validate(self, value: Any) -> Path:
212
+ """Pathオブジェクトに変換してから、VPathの検証を実行する。"""
213
+ try:
214
+ converted = Path(value)
215
+ except (TypeError, ValueError):
216
+ msg = f"'{self.public_name}'をpathlib.Pathオブジェクトに変換できませんでした: {value!r}。"
217
+ raise TypeError(msg) from None
218
+ return super().validate(converted)
219
+
220
+
221
+ class CString(VString, Converter[Any, str]):
222
+ """VStringの拡張バリデータ。
223
+
224
+ str型に変換可能なオブジェクトは例外を投げずに変換を行います。
225
+ """
226
+
227
+ def validate(self, value: Any) -> str:
228
+ tmp_value = value
229
+ if type(tmp_value) is not str:
230
+ try:
231
+ tmp_value = str(tmp_value)
232
+ except Exception:
233
+ msg = f"'{self.public_name}'はstr型に変換可能な値である必要があります: {tmp_value!r}。"
234
+ raise TypeError(msg) from None
235
+ return super().validate(tmp_value)
236
+
237
+
238
+ class CTimedelta(VTimedelta, Converter[Any, timedelta]):
239
+ """VTimedeltaの拡張バリデータです。
240
+
241
+ timedelta型に変換可能なオブジェクトは例外を投げずに変換を行います。
242
+ """
243
+
244
+ cmp_timedelta = re.compile(r"(?:(\d+) days?,? )?(\d+):(\d+):(\d+)(?:\.(\d+))?")
245
+
246
+ def validate(self, value: Any) -> timedelta:
247
+ f = None
248
+ if (tv := type(value)) is dict:
249
+ f = self.__try_convert_dict
250
+ elif tv in (list, tuple):
251
+ f = self.__try_convert_container
252
+ elif tv is str:
253
+ f = self.__try_convert_str
254
+ tmp_value = value
255
+ try:
256
+ if f is not None:
257
+ tmp_value = f(tmp_value)
258
+ except Exception:
259
+ msg = f"'{self.public_name}'はtimedelta型に変換可能な値である必要があります: {tmp_value!r}。"
260
+ raise TypeError(msg) from None
261
+ return super().validate(tmp_value)
262
+
263
+ def __try_convert_container(self, value: list | tuple) -> timedelta:
264
+ return timedelta(*value)
265
+
266
+ def __try_convert_dict(self, value: dict) -> timedelta:
267
+ return timedelta(**value)
268
+
269
+ def __try_convert_str(self, value: str) -> timedelta:
270
+ if (match := self.cmp_timedelta.fullmatch(value)) is not None:
271
+ keys = ("days", "hours", "minutes", "seconds", "microseconds")
272
+ d = {}
273
+ for k, v in zip(keys, match.groups(), strict=True):
274
+ if v is None:
275
+ continue
276
+ if k == "microseconds":
277
+ v = v.ljust(6, "0")[:6]
278
+ v = int(v)
279
+ d[k] = v
280
+ return self.__try_convert_dict(d)
281
+ msg = f"'{value}'をtimedelta型に変換できませんでした。"
282
+ raise ValueError(msg)
File without changes