nadeshiko-sdk 0.1.0__py3-none-any.whl

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.
Files changed (167) hide show
  1. nadeshiko/__init__.py +10 -0
  2. nadeshiko/_version.py +1 -0
  3. nadeshiko/api/__init__.py +1 -0
  4. nadeshiko/api/auth/__init__.py +3 -0
  5. nadeshiko/api/auth/internal/__init__.py +11 -0
  6. nadeshiko/api/auth/internal/get_discord_auth_url.py +176 -0
  7. nadeshiko/api/auth/internal/login.py +213 -0
  8. nadeshiko/api/auth/internal/login_discord.py +204 -0
  9. nadeshiko/api/auth/internal/login_google.py +204 -0
  10. nadeshiko/api/auth/internal/register.py +212 -0
  11. nadeshiko/api/auth_jwt/__init__.py +3 -0
  12. nadeshiko/api/auth_jwt/internal/__init__.py +7 -0
  13. nadeshiko/api/auth_jwt/internal/logout.py +151 -0
  14. nadeshiko/api/lists/__init__.py +8 -0
  15. nadeshiko/api/lists/internal/__init__.py +12 -0
  16. nadeshiko/api/lists/internal/list_add_item.py +215 -0
  17. nadeshiko/api/lists/internal/list_create.py +194 -0
  18. nadeshiko/api/lists/internal/list_destroy.py +193 -0
  19. nadeshiko/api/lists/internal/list_remove_item.py +207 -0
  20. nadeshiko/api/lists/internal/list_update.py +215 -0
  21. nadeshiko/api/lists/internal/list_update_item.py +229 -0
  22. nadeshiko/api/lists/list_index.py +257 -0
  23. nadeshiko/api/lists/list_show.py +193 -0
  24. nadeshiko/api/media/__init__.py +14 -0
  25. nadeshiko/api/media/character_show.py +193 -0
  26. nadeshiko/api/media/episode_index.py +229 -0
  27. nadeshiko/api/media/episode_show.py +207 -0
  28. nadeshiko/api/media/internal/__init__.py +16 -0
  29. nadeshiko/api/media/internal/episode_create.py +215 -0
  30. nadeshiko/api/media/internal/episode_destroy.py +205 -0
  31. nadeshiko/api/media/internal/episode_update.py +229 -0
  32. nadeshiko/api/media/internal/media_create.py +194 -0
  33. nadeshiko/api/media/internal/media_destroy.py +197 -0
  34. nadeshiko/api/media/internal/media_update.py +219 -0
  35. nadeshiko/api/media/internal/segment_create.py +229 -0
  36. nadeshiko/api/media/internal/segment_destroy.py +219 -0
  37. nadeshiko/api/media/internal/segment_index.py +243 -0
  38. nadeshiko/api/media/internal/segment_update.py +243 -0
  39. nadeshiko/api/media/media_index.py +232 -0
  40. nadeshiko/api/media/media_show.py +193 -0
  41. nadeshiko/api/media/segment_show.py +221 -0
  42. nadeshiko/api/media/segment_show_by_uuid.py +193 -0
  43. nadeshiko/api/media/seiyuu_show.py +193 -0
  44. nadeshiko/api/search/__init__.py +11 -0
  45. nadeshiko/api/search/fetch_media_info.py +278 -0
  46. nadeshiko/api/search/fetch_sentence_context.py +219 -0
  47. nadeshiko/api/search/internal/__init__.py +6 -0
  48. nadeshiko/api/search/search.py +295 -0
  49. nadeshiko/api/search/search_health_check.py +168 -0
  50. nadeshiko/api/search/search_multiple.py +238 -0
  51. nadeshiko/api/user/__init__.py +3 -0
  52. nadeshiko/api/user/internal/__init__.py +11 -0
  53. nadeshiko/api/user/internal/create_api_key.py +199 -0
  54. nadeshiko/api/user/internal/deactivate_api_key.py +194 -0
  55. nadeshiko/api/user/internal/get_api_keys.py +151 -0
  56. nadeshiko/api/user/internal/get_identity_me.py +156 -0
  57. nadeshiko/api/user/internal/get_user_info.py +156 -0
  58. nadeshiko/client.py +138 -0
  59. nadeshiko/errors.py +16 -0
  60. nadeshiko/models/__init__.py +215 -0
  61. nadeshiko/models/api_key.py +118 -0
  62. nadeshiko/models/api_key_permission.py +70 -0
  63. nadeshiko/models/auth_user.py +107 -0
  64. nadeshiko/models/basic_info.py +132 -0
  65. nadeshiko/models/category_statistic.py +71 -0
  66. nadeshiko/models/character.py +100 -0
  67. nadeshiko/models/character_input.py +130 -0
  68. nadeshiko/models/character_input_character_role.py +10 -0
  69. nadeshiko/models/character_with_media.py +124 -0
  70. nadeshiko/models/character_with_media_media_appearances_item.py +93 -0
  71. nadeshiko/models/character_with_media_media_appearances_item_role.py +10 -0
  72. nadeshiko/models/create_api_key_request.py +75 -0
  73. nadeshiko/models/create_api_key_response.py +70 -0
  74. nadeshiko/models/deactivate_api_key_request.py +62 -0
  75. nadeshiko/models/deactivate_api_key_response.py +62 -0
  76. nadeshiko/models/discord_auth_url_response.py +62 -0
  77. nadeshiko/models/discord_login_request.py +62 -0
  78. nadeshiko/models/episode.py +253 -0
  79. nadeshiko/models/episode_create_request.py +145 -0
  80. nadeshiko/models/episode_list_response.py +94 -0
  81. nadeshiko/models/episode_update_request.py +135 -0
  82. nadeshiko/models/error.py +128 -0
  83. nadeshiko/models/error_errors.py +46 -0
  84. nadeshiko/models/fetch_media_info_response.py +115 -0
  85. nadeshiko/models/fetch_media_info_type.py +10 -0
  86. nadeshiko/models/fetch_sentence_context_request.py +96 -0
  87. nadeshiko/models/fetch_sentence_context_response.py +75 -0
  88. nadeshiko/models/get_api_keys_response.py +86 -0
  89. nadeshiko/models/google_login_request.py +62 -0
  90. nadeshiko/models/list_.py +97 -0
  91. nadeshiko/models/list_add_item_body.py +69 -0
  92. nadeshiko/models/list_add_item_response_201.py +61 -0
  93. nadeshiko/models/list_create_request.py +107 -0
  94. nadeshiko/models/list_create_request_type.py +9 -0
  95. nadeshiko/models/list_create_request_visibility.py +9 -0
  96. nadeshiko/models/list_destroy_response_200.py +70 -0
  97. nadeshiko/models/list_index_type.py +9 -0
  98. nadeshiko/models/list_index_visibility.py +9 -0
  99. nadeshiko/models/list_input.py +138 -0
  100. nadeshiko/models/list_input_list_type.py +9 -0
  101. nadeshiko/models/list_input_list_visibility.py +9 -0
  102. nadeshiko/models/list_remove_item_response_200.py +61 -0
  103. nadeshiko/models/list_type.py +9 -0
  104. nadeshiko/models/list_update_body.py +78 -0
  105. nadeshiko/models/list_update_body_visibility.py +9 -0
  106. nadeshiko/models/list_update_item_body.py +61 -0
  107. nadeshiko/models/list_update_item_response_200.py +61 -0
  108. nadeshiko/models/list_visibility.py +9 -0
  109. nadeshiko/models/list_with_media.py +119 -0
  110. nadeshiko/models/list_with_media_media_item.py +83 -0
  111. nadeshiko/models/list_with_media_type.py +9 -0
  112. nadeshiko/models/list_with_media_visibility.py +9 -0
  113. nadeshiko/models/login_request.py +70 -0
  114. nadeshiko/models/login_response.py +84 -0
  115. nadeshiko/models/logout_response.py +73 -0
  116. nadeshiko/models/media.py +288 -0
  117. nadeshiko/models/media_category.py +11 -0
  118. nadeshiko/models/media_character.py +78 -0
  119. nadeshiko/models/media_character_role.py +10 -0
  120. nadeshiko/models/media_create_request.py +270 -0
  121. nadeshiko/models/media_create_request_category.py +11 -0
  122. nadeshiko/models/media_destroy_response_200.py +70 -0
  123. nadeshiko/models/media_index_category.py +9 -0
  124. nadeshiko/models/media_info_data.py +307 -0
  125. nadeshiko/models/media_info_path.py +83 -0
  126. nadeshiko/models/media_info_stats.py +89 -0
  127. nadeshiko/models/media_list_response.py +94 -0
  128. nadeshiko/models/media_update_request.py +297 -0
  129. nadeshiko/models/media_update_request_category.py +9 -0
  130. nadeshiko/models/quota_info.py +87 -0
  131. nadeshiko/models/quota_info_quota_limit_type_1.py +8 -0
  132. nadeshiko/models/register_request.py +78 -0
  133. nadeshiko/models/register_response.py +76 -0
  134. nadeshiko/models/register_response_user.py +46 -0
  135. nadeshiko/models/search_health_check_response.py +138 -0
  136. nadeshiko/models/search_multiple_request.py +72 -0
  137. nadeshiko/models/search_multiple_response.py +79 -0
  138. nadeshiko/models/search_request.py +253 -0
  139. nadeshiko/models/search_request_content_sort.py +13 -0
  140. nadeshiko/models/search_request_media_item.py +85 -0
  141. nadeshiko/models/search_request_media_item_seasons_item.py +69 -0
  142. nadeshiko/models/search_response.py +158 -0
  143. nadeshiko/models/segment.py +303 -0
  144. nadeshiko/models/segment_create_request.py +203 -0
  145. nadeshiko/models/segment_create_request_status.py +13 -0
  146. nadeshiko/models/segment_info.py +207 -0
  147. nadeshiko/models/segment_list_response.py +94 -0
  148. nadeshiko/models/segment_status.py +13 -0
  149. nadeshiko/models/segment_update_request.py +198 -0
  150. nadeshiko/models/segment_update_request_status.py +13 -0
  151. nadeshiko/models/seiyuu.py +86 -0
  152. nadeshiko/models/seiyuu_with_roles.py +108 -0
  153. nadeshiko/models/seiyuu_with_roles_roles_item.py +109 -0
  154. nadeshiko/models/seiyuu_with_roles_roles_item_role.py +10 -0
  155. nadeshiko/models/sentence.py +88 -0
  156. nadeshiko/models/statistic.py +132 -0
  157. nadeshiko/models/statistic_season_with_episode_hits.py +74 -0
  158. nadeshiko/models/statistic_season_with_episode_hits_additional_property.py +46 -0
  159. nadeshiko/models/user_info_response.py +79 -0
  160. nadeshiko/models/user_info_response_user.py +91 -0
  161. nadeshiko/models/user_role.py +71 -0
  162. nadeshiko/models/word_match.py +107 -0
  163. nadeshiko/models/word_match_media.py +98 -0
  164. nadeshiko/types.py +54 -0
  165. nadeshiko_sdk-0.1.0.dist-info/METADATA +147 -0
  166. nadeshiko_sdk-0.1.0.dist-info/RECORD +167 -0
  167. nadeshiko_sdk-0.1.0.dist-info/WHEEL +4 -0
@@ -0,0 +1,91 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Mapping
4
+ from typing import TYPE_CHECKING, Any, TypeVar
5
+
6
+ from attrs import define as _attrs_define
7
+ from attrs import field as _attrs_field
8
+
9
+ if TYPE_CHECKING:
10
+ from ..models.user_role import UserRole
11
+
12
+
13
+ T = TypeVar("T", bound="UserInfoResponseUser")
14
+
15
+
16
+ @_attrs_define
17
+ class UserInfoResponseUser:
18
+ """
19
+ Attributes:
20
+ username (str): User's display name
21
+ email (str): User's email address
22
+ roles (list[UserRole]): User's roles
23
+ """
24
+
25
+ username: str
26
+ email: str
27
+ roles: list[UserRole]
28
+ additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
29
+
30
+ def to_dict(self) -> dict[str, Any]:
31
+ username = self.username
32
+
33
+ email = self.email
34
+
35
+ roles = []
36
+ for roles_item_data in self.roles:
37
+ roles_item = roles_item_data.to_dict()
38
+ roles.append(roles_item)
39
+
40
+ field_dict: dict[str, Any] = {}
41
+ field_dict.update(self.additional_properties)
42
+ field_dict.update(
43
+ {
44
+ "username": username,
45
+ "email": email,
46
+ "roles": roles,
47
+ }
48
+ )
49
+
50
+ return field_dict
51
+
52
+ @classmethod
53
+ def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
54
+ from ..models.user_role import UserRole
55
+
56
+ d = dict(src_dict)
57
+ username = d.pop("username")
58
+
59
+ email = d.pop("email")
60
+
61
+ roles = []
62
+ _roles = d.pop("roles")
63
+ for roles_item_data in _roles:
64
+ roles_item = UserRole.from_dict(roles_item_data)
65
+
66
+ roles.append(roles_item)
67
+
68
+ user_info_response_user = cls(
69
+ username=username,
70
+ email=email,
71
+ roles=roles,
72
+ )
73
+
74
+ user_info_response_user.additional_properties = d
75
+ return user_info_response_user
76
+
77
+ @property
78
+ def additional_keys(self) -> list[str]:
79
+ return list(self.additional_properties.keys())
80
+
81
+ def __getitem__(self, key: str) -> Any:
82
+ return self.additional_properties[key]
83
+
84
+ def __setitem__(self, key: str, value: Any) -> None:
85
+ self.additional_properties[key] = value
86
+
87
+ def __delitem__(self, key: str) -> None:
88
+ del self.additional_properties[key]
89
+
90
+ def __contains__(self, key: str) -> bool:
91
+ return key in self.additional_properties
@@ -0,0 +1,71 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Mapping
4
+ from typing import Any, TypeVar
5
+
6
+ from attrs import define as _attrs_define
7
+ from attrs import field as _attrs_field
8
+
9
+ from ..types import UNSET, Unset
10
+
11
+ T = TypeVar("T", bound="UserRole")
12
+
13
+
14
+ @_attrs_define
15
+ class UserRole:
16
+ """User role information
17
+
18
+ Attributes:
19
+ id_role (int | Unset): Role ID
20
+ name (str | Unset): Role name
21
+ """
22
+
23
+ id_role: int | Unset = UNSET
24
+ name: str | Unset = UNSET
25
+ additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
26
+
27
+ def to_dict(self) -> dict[str, Any]:
28
+ id_role = self.id_role
29
+
30
+ name = self.name
31
+
32
+ field_dict: dict[str, Any] = {}
33
+ field_dict.update(self.additional_properties)
34
+ field_dict.update({})
35
+ if id_role is not UNSET:
36
+ field_dict["id_role"] = id_role
37
+ if name is not UNSET:
38
+ field_dict["name"] = name
39
+
40
+ return field_dict
41
+
42
+ @classmethod
43
+ def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
44
+ d = dict(src_dict)
45
+ id_role = d.pop("id_role", UNSET)
46
+
47
+ name = d.pop("name", UNSET)
48
+
49
+ user_role = cls(
50
+ id_role=id_role,
51
+ name=name,
52
+ )
53
+
54
+ user_role.additional_properties = d
55
+ return user_role
56
+
57
+ @property
58
+ def additional_keys(self) -> list[str]:
59
+ return list(self.additional_properties.keys())
60
+
61
+ def __getitem__(self, key: str) -> Any:
62
+ return self.additional_properties[key]
63
+
64
+ def __setitem__(self, key: str, value: Any) -> None:
65
+ self.additional_properties[key] = value
66
+
67
+ def __delitem__(self, key: str) -> None:
68
+ del self.additional_properties[key]
69
+
70
+ def __contains__(self, key: str) -> bool:
71
+ return key in self.additional_properties
@@ -0,0 +1,107 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Mapping
4
+ from typing import TYPE_CHECKING, Any, TypeVar
5
+
6
+ from attrs import define as _attrs_define
7
+ from attrs import field as _attrs_field
8
+
9
+ from ..types import UNSET, Unset
10
+
11
+ if TYPE_CHECKING:
12
+ from ..models.word_match_media import WordMatchMedia
13
+
14
+
15
+ T = TypeVar("T", bound="WordMatch")
16
+
17
+
18
+ @_attrs_define
19
+ class WordMatch:
20
+ """Word matching result with occurrences across media
21
+
22
+ Attributes:
23
+ word (str | Unset): The word that was searched for Example: 彼女.
24
+ is_match (bool | Unset): Indicates whether the word was found in any segment Example: True.
25
+ total_matches (int | Unset): Total number of times this word appears across all media Example: 1523.
26
+ media (list[WordMatchMedia] | Unset): List of media containing this word
27
+ """
28
+
29
+ word: str | Unset = UNSET
30
+ is_match: bool | Unset = UNSET
31
+ total_matches: int | Unset = UNSET
32
+ media: list[WordMatchMedia] | Unset = UNSET
33
+ additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
34
+
35
+ def to_dict(self) -> dict[str, Any]:
36
+ word = self.word
37
+
38
+ is_match = self.is_match
39
+
40
+ total_matches = self.total_matches
41
+
42
+ media: list[dict[str, Any]] | Unset = UNSET
43
+ if not isinstance(self.media, Unset):
44
+ media = []
45
+ for media_item_data in self.media:
46
+ media_item = media_item_data.to_dict()
47
+ media.append(media_item)
48
+
49
+ field_dict: dict[str, Any] = {}
50
+ field_dict.update(self.additional_properties)
51
+ field_dict.update({})
52
+ if word is not UNSET:
53
+ field_dict["word"] = word
54
+ if is_match is not UNSET:
55
+ field_dict["is_match"] = is_match
56
+ if total_matches is not UNSET:
57
+ field_dict["total_matches"] = total_matches
58
+ if media is not UNSET:
59
+ field_dict["media"] = media
60
+
61
+ return field_dict
62
+
63
+ @classmethod
64
+ def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
65
+ from ..models.word_match_media import WordMatchMedia
66
+
67
+ d = dict(src_dict)
68
+ word = d.pop("word", UNSET)
69
+
70
+ is_match = d.pop("is_match", UNSET)
71
+
72
+ total_matches = d.pop("total_matches", UNSET)
73
+
74
+ _media = d.pop("media", UNSET)
75
+ media: list[WordMatchMedia] | Unset = UNSET
76
+ if _media is not UNSET:
77
+ media = []
78
+ for media_item_data in _media:
79
+ media_item = WordMatchMedia.from_dict(media_item_data)
80
+
81
+ media.append(media_item)
82
+
83
+ word_match = cls(
84
+ word=word,
85
+ is_match=is_match,
86
+ total_matches=total_matches,
87
+ media=media,
88
+ )
89
+
90
+ word_match.additional_properties = d
91
+ return word_match
92
+
93
+ @property
94
+ def additional_keys(self) -> list[str]:
95
+ return list(self.additional_properties.keys())
96
+
97
+ def __getitem__(self, key: str) -> Any:
98
+ return self.additional_properties[key]
99
+
100
+ def __setitem__(self, key: str, value: Any) -> None:
101
+ self.additional_properties[key] = value
102
+
103
+ def __delitem__(self, key: str) -> None:
104
+ del self.additional_properties[key]
105
+
106
+ def __contains__(self, key: str) -> bool:
107
+ return key in self.additional_properties
@@ -0,0 +1,98 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Mapping
4
+ from typing import Any, TypeVar
5
+
6
+ from attrs import define as _attrs_define
7
+ from attrs import field as _attrs_field
8
+
9
+ from ..types import UNSET, Unset
10
+
11
+ T = TypeVar("T", bound="WordMatchMedia")
12
+
13
+
14
+ @_attrs_define
15
+ class WordMatchMedia:
16
+ """Media entry containing word matches
17
+
18
+ Attributes:
19
+ media_id (int | Unset): Unique identifier for the media Example: 110316.
20
+ english_name (str | Unset): English translation of the media name Example: Steins;Gate.
21
+ japanese_name (str | Unset): Original Japanese name of the media Example: シュタインズ・ゲート.
22
+ romaji_name (str | Unset): Romaji transliteration of the media name Example: Steins;Gate.
23
+ matches (int | Unset): Number of times the word appears in this media Example: 234.
24
+ """
25
+
26
+ media_id: int | Unset = UNSET
27
+ english_name: str | Unset = UNSET
28
+ japanese_name: str | Unset = UNSET
29
+ romaji_name: str | Unset = UNSET
30
+ matches: int | Unset = UNSET
31
+ additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
32
+
33
+ def to_dict(self) -> dict[str, Any]:
34
+ media_id = self.media_id
35
+
36
+ english_name = self.english_name
37
+
38
+ japanese_name = self.japanese_name
39
+
40
+ romaji_name = self.romaji_name
41
+
42
+ matches = self.matches
43
+
44
+ field_dict: dict[str, Any] = {}
45
+ field_dict.update(self.additional_properties)
46
+ field_dict.update({})
47
+ if media_id is not UNSET:
48
+ field_dict["media_id"] = media_id
49
+ if english_name is not UNSET:
50
+ field_dict["english_name"] = english_name
51
+ if japanese_name is not UNSET:
52
+ field_dict["japanese_name"] = japanese_name
53
+ if romaji_name is not UNSET:
54
+ field_dict["romaji_name"] = romaji_name
55
+ if matches is not UNSET:
56
+ field_dict["matches"] = matches
57
+
58
+ return field_dict
59
+
60
+ @classmethod
61
+ def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
62
+ d = dict(src_dict)
63
+ media_id = d.pop("media_id", UNSET)
64
+
65
+ english_name = d.pop("english_name", UNSET)
66
+
67
+ japanese_name = d.pop("japanese_name", UNSET)
68
+
69
+ romaji_name = d.pop("romaji_name", UNSET)
70
+
71
+ matches = d.pop("matches", UNSET)
72
+
73
+ word_match_media = cls(
74
+ media_id=media_id,
75
+ english_name=english_name,
76
+ japanese_name=japanese_name,
77
+ romaji_name=romaji_name,
78
+ matches=matches,
79
+ )
80
+
81
+ word_match_media.additional_properties = d
82
+ return word_match_media
83
+
84
+ @property
85
+ def additional_keys(self) -> list[str]:
86
+ return list(self.additional_properties.keys())
87
+
88
+ def __getitem__(self, key: str) -> Any:
89
+ return self.additional_properties[key]
90
+
91
+ def __setitem__(self, key: str, value: Any) -> None:
92
+ self.additional_properties[key] = value
93
+
94
+ def __delitem__(self, key: str) -> None:
95
+ del self.additional_properties[key]
96
+
97
+ def __contains__(self, key: str) -> bool:
98
+ return key in self.additional_properties
nadeshiko/types.py ADDED
@@ -0,0 +1,54 @@
1
+ """Contains some shared types for properties"""
2
+
3
+ from collections.abc import Mapping, MutableMapping
4
+ from http import HTTPStatus
5
+ from typing import IO, BinaryIO, Generic, Literal, TypeVar
6
+
7
+ from attrs import define
8
+
9
+
10
+ class Unset:
11
+ def __bool__(self) -> Literal[False]:
12
+ return False
13
+
14
+
15
+ UNSET: Unset = Unset()
16
+
17
+ # The types that `httpx.Client(files=)` can accept, copied from that library.
18
+ FileContent = IO[bytes] | bytes | str
19
+ FileTypes = (
20
+ # (filename, file (or bytes), content_type)
21
+ tuple[str | None, FileContent, str | None]
22
+ # (filename, file (or bytes), content_type, headers)
23
+ | tuple[str | None, FileContent, str | None, Mapping[str, str]]
24
+ )
25
+ RequestFiles = list[tuple[str, FileTypes]]
26
+
27
+
28
+ @define
29
+ class File:
30
+ """Contains information for file uploads"""
31
+
32
+ payload: BinaryIO
33
+ file_name: str | None = None
34
+ mime_type: str | None = None
35
+
36
+ def to_tuple(self) -> FileTypes:
37
+ """Return a tuple representation that httpx will accept for multipart/form-data"""
38
+ return self.file_name, self.payload, self.mime_type
39
+
40
+
41
+ T = TypeVar("T")
42
+
43
+
44
+ @define
45
+ class Response(Generic[T]):
46
+ """A response from an endpoint"""
47
+
48
+ status_code: HTTPStatus
49
+ content: bytes
50
+ headers: MutableMapping[str, str]
51
+ parsed: T | None
52
+
53
+
54
+ __all__ = ["UNSET", "File", "FileTypes", "RequestFiles", "Response", "Unset"]
@@ -0,0 +1,147 @@
1
+ Metadata-Version: 2.4
2
+ Name: nadeshiko-sdk
3
+ Version: 0.1.0
4
+ Summary: Python SDK for Nadeshiko API
5
+ Requires-Python: >=3.11
6
+ Requires-Dist: attrs>=23.0
7
+ Requires-Dist: httpx>=0.27.0
8
+ Requires-Dist: python-dateutil>=2.8.0
9
+ Provides-Extra: dev
10
+ Requires-Dist: openapi-python-client>=0.22.0; extra == 'dev'
11
+ Requires-Dist: pytest>=9.0.0; extra == 'dev'
12
+ Requires-Dist: pyyaml>=6.0; extra == 'dev'
13
+ Requires-Dist: ruff>=0.9.0; extra == 'dev'
14
+ Description-Content-Type: text/markdown
15
+
16
+ > This repository is still in WIP and not ready for production use
17
+
18
+ # Nadeshiko Python SDK
19
+
20
+ Python SDK for the [Nadeshiko API](https://nadeshiko.co)
21
+
22
+ ## Quick Start
23
+
24
+ ```python
25
+ from nadeshiko import Nadeshiko, Environment
26
+ from nadeshiko.api.search import search
27
+
28
+ # Configure your client
29
+ client = Nadeshiko(
30
+ api_key='your-api-key-here',
31
+ base_url=Environment.PRODUCTION, # or Environment.LOCAL, or custom URL
32
+ )
33
+
34
+ # Use API methods (names match OpenAPI operationIds)
35
+ result = search.sync(
36
+ client=client,
37
+ body={
38
+ 'query': '彼女',
39
+ 'limit': 10,
40
+ },
41
+ )
42
+
43
+ if isinstance(result, Error):
44
+ print(result.code, result.detail)
45
+ else:
46
+ print(result.sentences)
47
+ ```
48
+
49
+ ## API Methods
50
+
51
+ All methods are organized under `nadeshiko.api.{module}` and match the OpenAPI operationIds exactly:
52
+
53
+ You can check the full specification from the [OpenAPI spec page](https://nadeshiko.co/api/v1/docs).
54
+
55
+ ## Error Handling
56
+
57
+ All methods return a union type: `Response | Error | None`.
58
+
59
+ **Check for errors:**
60
+ ```python
61
+ from nadeshiko import Nadeshiko, Environment
62
+ from nadeshiko.api.search import search
63
+ from nadeshiko.models import Error
64
+
65
+ client = Nadeshiko(api_key='your-api-key', base_url=Environment.PRODUCTION)
66
+ result = search.sync(client=client, body={'query': '彼女'})
67
+
68
+ if isinstance(result, Error):
69
+ # Error type is fully generated from OpenAPI spec
70
+ print(result.code) # e.g., 'RATE_LIMIT_EXCEEDED'
71
+ print(result.title) # e.g., 'Rate Limit Exceeded'
72
+ print(result.detail) # Detailed message
73
+ print(result.status) # HTTP status code
74
+ else:
75
+ print(result.sentences)
76
+ ```
77
+
78
+ In general, all SDK methods return typed errors generated from the OpenAPI spec:
79
+
80
+ ```python
81
+ class Error:
82
+ code: str # e.g., 'RATE_LIMIT_EXCEEDED', 'AUTH_CREDENTIALS_INVALID'
83
+ title: str # Short summary
84
+ detail: str # Detailed explanation
85
+ status: int # HTTP status code
86
+ type_: str | Unset # URI to error documentation
87
+ instance: str | Unset # Trace ID
88
+ errors: dict | Unset # Validation errors
89
+ ```
90
+
91
+ Handle each error independently based on the error code returned by the API.
92
+
93
+ ```python
94
+ from nadeshiko import Nadeshiko, Environment
95
+ from nadeshiko.api.search import search
96
+ from nadeshiko.models import Error
97
+
98
+ client = Nadeshiko(api_key='your-api-key')
99
+ result = search.sync(client=client, body={'query': '彼女'})
100
+
101
+ if isinstance(result, Error):
102
+ # All error fields are typed
103
+ match result.code:
104
+ case 'RATE_LIMIT_EXCEEDED':
105
+ print('Wait before retrying')
106
+ case 'AUTH_CREDENTIALS_INVALID':
107
+ print('Check your API key')
108
+ case 'VALIDATION_FAILED':
109
+ print('Field errors:', result.errors)
110
+ case _:
111
+ print(result.detail)
112
+ ```
113
+
114
+ You can check the full list of errors codes for each endpoint from the [OpenAPI spec page](https://nadeshiko.co/api/v1/docs).
115
+
116
+ ## Type Support
117
+
118
+ All types are auto-generated from the OpenAPI spec.
119
+
120
+ ```python
121
+ from nadeshiko.models import (
122
+ SearchRequest,
123
+ SearchResponse,
124
+ Sentence,
125
+ MediaInfoData,
126
+ )
127
+
128
+ request: SearchRequest = {
129
+ 'query': '彼女',
130
+ 'limit': 10,
131
+ }
132
+
133
+ sentence: Sentence = {
134
+ 'basic_info': { # ... },
135
+ 'segment_info': { # ... },
136
+ 'media_info': { # ... },
137
+ }
138
+ ```
139
+
140
+ ## Examples
141
+
142
+ See `examples/usage.py` for more usage examples.
143
+
144
+ ## References
145
+
146
+ - [Nadeshiko Website](https://nadeshiko.co)
147
+ - [API Documentation](https://nadeshiko.co/settings/api)