ynab-cli 0.3.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 (206) hide show
  1. ynab_cli/__init__.py +0 -0
  2. ynab_cli/adapters/__init__.py +0 -0
  3. ynab_cli/adapters/rich/__init__.py +0 -0
  4. ynab_cli/adapters/rich/io.py +38 -0
  5. ynab_cli/adapters/textual/__init__.py +0 -0
  6. ynab_cli/adapters/textual/io.py +62 -0
  7. ynab_cli/adapters/ynab/__init__.py +8 -0
  8. ynab_cli/adapters/ynab/api/__init__.py +1 -0
  9. ynab_cli/adapters/ynab/api/accounts/__init__.py +1 -0
  10. ynab_cli/adapters/ynab/api/accounts/create_account.py +186 -0
  11. ynab_cli/adapters/ynab/api/accounts/get_account_by_id.py +177 -0
  12. ynab_cli/adapters/ynab/api/accounts/get_accounts.py +184 -0
  13. ynab_cli/adapters/ynab/api/budgets/__init__.py +1 -0
  14. ynab_cli/adapters/ynab/api/budgets/get_budget_by_id.py +188 -0
  15. ynab_cli/adapters/ynab/api/budgets/get_budget_settings_by_id.py +163 -0
  16. ynab_cli/adapters/ynab/api/budgets/get_budgets.py +171 -0
  17. ynab_cli/adapters/ynab/api/categories/__init__.py +1 -0
  18. ynab_cli/adapters/ynab/api/categories/get_categories.py +188 -0
  19. ynab_cli/adapters/ynab/api/categories/get_category_by_id.py +180 -0
  20. ynab_cli/adapters/ynab/api/categories/get_month_category_by_id.py +194 -0
  21. ynab_cli/adapters/ynab/api/categories/update_category.py +199 -0
  22. ynab_cli/adapters/ynab/api/categories/update_month_category.py +213 -0
  23. ynab_cli/adapters/ynab/api/months/__init__.py +1 -0
  24. ynab_cli/adapters/ynab/api/months/get_budget_month.py +177 -0
  25. ynab_cli/adapters/ynab/api/months/get_budget_months.py +184 -0
  26. ynab_cli/adapters/ynab/api/payee_locations/__init__.py +1 -0
  27. ynab_cli/adapters/ynab/api/payee_locations/get_payee_location_by_id.py +176 -0
  28. ynab_cli/adapters/ynab/api/payee_locations/get_payee_locations.py +163 -0
  29. ynab_cli/adapters/ynab/api/payee_locations/get_payee_locations_by_payee.py +176 -0
  30. ynab_cli/adapters/ynab/api/payees/__init__.py +1 -0
  31. ynab_cli/adapters/ynab/api/payees/get_payee_by_id.py +176 -0
  32. ynab_cli/adapters/ynab/api/payees/get_payees.py +184 -0
  33. ynab_cli/adapters/ynab/api/payees/update_payee.py +199 -0
  34. ynab_cli/adapters/ynab/api/scheduled_transactions/__init__.py +1 -0
  35. ynab_cli/adapters/ynab/api/scheduled_transactions/create_scheduled_transaction.py +186 -0
  36. ynab_cli/adapters/ynab/api/scheduled_transactions/delete_scheduled_transaction.py +176 -0
  37. ynab_cli/adapters/ynab/api/scheduled_transactions/get_scheduled_transaction_by_id.py +176 -0
  38. ynab_cli/adapters/ynab/api/scheduled_transactions/get_scheduled_transactions.py +184 -0
  39. ynab_cli/adapters/ynab/api/scheduled_transactions/update_scheduled_transaction.py +199 -0
  40. ynab_cli/adapters/ynab/api/transactions/__init__.py +1 -0
  41. ynab_cli/adapters/ynab/api/transactions/create_transaction.py +202 -0
  42. ynab_cli/adapters/ynab/api/transactions/delete_transaction.py +176 -0
  43. ynab_cli/adapters/ynab/api/transactions/get_transaction_by_id.py +176 -0
  44. ynab_cli/adapters/ynab/api/transactions/get_transactions.py +227 -0
  45. ynab_cli/adapters/ynab/api/transactions/get_transactions_by_account.py +236 -0
  46. ynab_cli/adapters/ynab/api/transactions/get_transactions_by_category.py +236 -0
  47. ynab_cli/adapters/ynab/api/transactions/get_transactions_by_month.py +236 -0
  48. ynab_cli/adapters/ynab/api/transactions/get_transactions_by_payee.py +236 -0
  49. ynab_cli/adapters/ynab/api/transactions/import_transactions.py +183 -0
  50. ynab_cli/adapters/ynab/api/transactions/update_transaction.py +199 -0
  51. ynab_cli/adapters/ynab/api/transactions/update_transactions.py +177 -0
  52. ynab_cli/adapters/ynab/api/user/__init__.py +1 -0
  53. ynab_cli/adapters/ynab/api/user/get_user.py +130 -0
  54. ynab_cli/adapters/ynab/client.py +268 -0
  55. ynab_cli/adapters/ynab/errors.py +16 -0
  56. ynab_cli/adapters/ynab/models/__init__.py +229 -0
  57. ynab_cli/adapters/ynab/models/account.py +345 -0
  58. ynab_cli/adapters/ynab/models/account_response.py +65 -0
  59. ynab_cli/adapters/ynab/models/account_response_data.py +65 -0
  60. ynab_cli/adapters/ynab/models/account_type.py +23 -0
  61. ynab_cli/adapters/ynab/models/accounts_response.py +65 -0
  62. ynab_cli/adapters/ynab/models/accounts_response_data.py +81 -0
  63. ynab_cli/adapters/ynab/models/budget_detail.py +405 -0
  64. ynab_cli/adapters/ynab/models/budget_detail_response.py +65 -0
  65. ynab_cli/adapters/ynab/models/budget_detail_response_data.py +73 -0
  66. ynab_cli/adapters/ynab/models/budget_settings.py +115 -0
  67. ynab_cli/adapters/ynab/models/budget_settings_response.py +65 -0
  68. ynab_cli/adapters/ynab/models/budget_settings_response_data.py +65 -0
  69. ynab_cli/adapters/ynab/models/budget_summary.py +216 -0
  70. ynab_cli/adapters/ynab/models/budget_summary_response.py +65 -0
  71. ynab_cli/adapters/ynab/models/budget_summary_response_data.py +91 -0
  72. ynab_cli/adapters/ynab/models/bulk_response.py +65 -0
  73. ynab_cli/adapters/ynab/models/bulk_response_data.py +65 -0
  74. ynab_cli/adapters/ynab/models/bulk_response_data_bulk.py +68 -0
  75. ynab_cli/adapters/ynab/models/bulk_transactions.py +75 -0
  76. ynab_cli/adapters/ynab/models/categories_response.py +65 -0
  77. ynab_cli/adapters/ynab/models/categories_response_data.py +81 -0
  78. ynab_cli/adapters/ynab/models/category.py +519 -0
  79. ynab_cli/adapters/ynab/models/category_goal_type_type_1.py +15 -0
  80. ynab_cli/adapters/ynab/models/category_goal_type_type_2_type_1.py +15 -0
  81. ynab_cli/adapters/ynab/models/category_goal_type_type_3_type_1.py +15 -0
  82. ynab_cli/adapters/ynab/models/category_group.py +85 -0
  83. ynab_cli/adapters/ynab/models/category_group_with_categories.py +108 -0
  84. ynab_cli/adapters/ynab/models/category_response.py +65 -0
  85. ynab_cli/adapters/ynab/models/category_response_data.py +65 -0
  86. ynab_cli/adapters/ynab/models/currency_format_type_0.py +117 -0
  87. ynab_cli/adapters/ynab/models/date_format_type_0.py +61 -0
  88. ynab_cli/adapters/ynab/models/error_detail.py +75 -0
  89. ynab_cli/adapters/ynab/models/error_response.py +65 -0
  90. ynab_cli/adapters/ynab/models/existing_transaction.py +322 -0
  91. ynab_cli/adapters/ynab/models/get_transactions_by_account_type.py +12 -0
  92. ynab_cli/adapters/ynab/models/get_transactions_by_category_type.py +12 -0
  93. ynab_cli/adapters/ynab/models/get_transactions_by_month_type.py +12 -0
  94. ynab_cli/adapters/ynab/models/get_transactions_by_payee_type.py +12 -0
  95. ynab_cli/adapters/ynab/models/get_transactions_type.py +12 -0
  96. ynab_cli/adapters/ynab/models/hybrid_transaction.py +567 -0
  97. ynab_cli/adapters/ynab/models/hybrid_transaction_type.py +12 -0
  98. ynab_cli/adapters/ynab/models/hybrid_transactions_response.py +65 -0
  99. ynab_cli/adapters/ynab/models/hybrid_transactions_response_data.py +84 -0
  100. ynab_cli/adapters/ynab/models/loan_account_periodic_value_type_0.py +44 -0
  101. ynab_cli/adapters/ynab/models/month_detail.py +168 -0
  102. ynab_cli/adapters/ynab/models/month_detail_response.py +65 -0
  103. ynab_cli/adapters/ynab/models/month_detail_response_data.py +65 -0
  104. ynab_cli/adapters/ynab/models/month_summaries_response.py +65 -0
  105. ynab_cli/adapters/ynab/models/month_summaries_response_data.py +81 -0
  106. ynab_cli/adapters/ynab/models/month_summary.py +145 -0
  107. ynab_cli/adapters/ynab/models/new_transaction.py +352 -0
  108. ynab_cli/adapters/ynab/models/patch_category_wrapper.py +65 -0
  109. ynab_cli/adapters/ynab/models/patch_month_category_wrapper.py +65 -0
  110. ynab_cli/adapters/ynab/models/patch_payee_wrapper.py +65 -0
  111. ynab_cli/adapters/ynab/models/patch_transactions_wrapper.py +73 -0
  112. ynab_cli/adapters/ynab/models/payee.py +100 -0
  113. ynab_cli/adapters/ynab/models/payee_location.py +93 -0
  114. ynab_cli/adapters/ynab/models/payee_location_response.py +65 -0
  115. ynab_cli/adapters/ynab/models/payee_location_response_data.py +65 -0
  116. ynab_cli/adapters/ynab/models/payee_locations_response.py +65 -0
  117. ynab_cli/adapters/ynab/models/payee_locations_response_data.py +73 -0
  118. ynab_cli/adapters/ynab/models/payee_response.py +65 -0
  119. ynab_cli/adapters/ynab/models/payee_response_data.py +65 -0
  120. ynab_cli/adapters/ynab/models/payees_response.py +65 -0
  121. ynab_cli/adapters/ynab/models/payees_response_data.py +81 -0
  122. ynab_cli/adapters/ynab/models/post_account_wrapper.py +65 -0
  123. ynab_cli/adapters/ynab/models/post_scheduled_transaction_wrapper.py +65 -0
  124. ynab_cli/adapters/ynab/models/post_transactions_wrapper.py +91 -0
  125. ynab_cli/adapters/ynab/models/put_scheduled_transaction_wrapper.py +65 -0
  126. ynab_cli/adapters/ynab/models/put_transaction_wrapper.py +65 -0
  127. ynab_cli/adapters/ynab/models/save_account.py +77 -0
  128. ynab_cli/adapters/ynab/models/save_category.py +129 -0
  129. ynab_cli/adapters/ynab/models/save_category_response.py +65 -0
  130. ynab_cli/adapters/ynab/models/save_category_response_data.py +73 -0
  131. ynab_cli/adapters/ynab/models/save_month_category.py +59 -0
  132. ynab_cli/adapters/ynab/models/save_payee.py +59 -0
  133. ynab_cli/adapters/ynab/models/save_payee_response.py +65 -0
  134. ynab_cli/adapters/ynab/models/save_payee_response_data.py +73 -0
  135. ynab_cli/adapters/ynab/models/save_scheduled_transaction.py +269 -0
  136. ynab_cli/adapters/ynab/models/save_sub_transaction.py +166 -0
  137. ynab_cli/adapters/ynab/models/save_transaction_with_id_or_import_id.py +366 -0
  138. ynab_cli/adapters/ynab/models/save_transaction_with_optional_fields.py +322 -0
  139. ynab_cli/adapters/ynab/models/save_transactions_response.py +65 -0
  140. ynab_cli/adapters/ynab/models/save_transactions_response_data.py +123 -0
  141. ynab_cli/adapters/ynab/models/scheduled_sub_transaction.py +238 -0
  142. ynab_cli/adapters/ynab/models/scheduled_transaction_detail.py +381 -0
  143. ynab_cli/adapters/ynab/models/scheduled_transaction_frequency.py +23 -0
  144. ynab_cli/adapters/ynab/models/scheduled_transaction_response.py +65 -0
  145. ynab_cli/adapters/ynab/models/scheduled_transaction_response_data.py +65 -0
  146. ynab_cli/adapters/ynab/models/scheduled_transaction_summary.py +310 -0
  147. ynab_cli/adapters/ynab/models/scheduled_transaction_summary_frequency.py +23 -0
  148. ynab_cli/adapters/ynab/models/scheduled_transactions_response.py +65 -0
  149. ynab_cli/adapters/ynab/models/scheduled_transactions_response_data.py +81 -0
  150. ynab_cli/adapters/ynab/models/sub_transaction.py +259 -0
  151. ynab_cli/adapters/ynab/models/transaction_cleared_status.py +13 -0
  152. ynab_cli/adapters/ynab/models/transaction_detail.py +570 -0
  153. ynab_cli/adapters/ynab/models/transaction_flag_color_type_1.py +16 -0
  154. ynab_cli/adapters/ynab/models/transaction_flag_color_type_2_type_1.py +16 -0
  155. ynab_cli/adapters/ynab/models/transaction_flag_color_type_3_type_1.py +16 -0
  156. ynab_cli/adapters/ynab/models/transaction_response.py +65 -0
  157. ynab_cli/adapters/ynab/models/transaction_response_data.py +65 -0
  158. ynab_cli/adapters/ynab/models/transaction_summary.py +499 -0
  159. ynab_cli/adapters/ynab/models/transaction_summary_debt_transaction_type_type_1.py +18 -0
  160. ynab_cli/adapters/ynab/models/transaction_summary_debt_transaction_type_type_2_type_1.py +18 -0
  161. ynab_cli/adapters/ynab/models/transaction_summary_debt_transaction_type_type_3_type_1.py +18 -0
  162. ynab_cli/adapters/ynab/models/transactions_import_response.py +65 -0
  163. ynab_cli/adapters/ynab/models/transactions_import_response_data.py +59 -0
  164. ynab_cli/adapters/ynab/models/transactions_response.py +65 -0
  165. ynab_cli/adapters/ynab/models/transactions_response_data.py +81 -0
  166. ynab_cli/adapters/ynab/models/user.py +60 -0
  167. ynab_cli/adapters/ynab/models/user_response.py +65 -0
  168. ynab_cli/adapters/ynab/models/user_response_data.py +65 -0
  169. ynab_cli/adapters/ynab/types.py +46 -0
  170. ynab_cli/adapters/ynab/util.py +78 -0
  171. ynab_cli/domain/__init__.py +0 -0
  172. ynab_cli/domain/constants.py +3 -0
  173. ynab_cli/domain/models/__init__.py +0 -0
  174. ynab_cli/domain/models/rules.py +37 -0
  175. ynab_cli/domain/ports/__init__.py +5 -0
  176. ynab_cli/domain/ports/io.py +63 -0
  177. ynab_cli/domain/settings.py +16 -0
  178. ynab_cli/domain/use_cases/__init__.py +0 -0
  179. ynab_cli/domain/use_cases/categories.py +105 -0
  180. ynab_cli/domain/use_cases/payees.py +232 -0
  181. ynab_cli/domain/use_cases/transactions.py +93 -0
  182. ynab_cli/host/__init__.py +14 -0
  183. ynab_cli/host/cli.py +37 -0
  184. ynab_cli/host/click/__init__.py +0 -0
  185. ynab_cli/host/click/cli.py +44 -0
  186. ynab_cli/host/click/commands/__init__.py +0 -0
  187. ynab_cli/host/click/commands/categories.py +92 -0
  188. ynab_cli/host/click/commands/payees.py +157 -0
  189. ynab_cli/host/click/commands/rich/__init__.py +0 -0
  190. ynab_cli/host/click/commands/rich/progress_table.py +37 -0
  191. ynab_cli/host/click/commands/transactions.py +76 -0
  192. ynab_cli/host/constants.py +3 -0
  193. ynab_cli/host/textual/__init__.py +0 -0
  194. ynab_cli/host/textual/app.py +98 -0
  195. ynab_cli/host/textual/app.tcss +47 -0
  196. ynab_cli/host/textual/cli.py +42 -0
  197. ynab_cli/host/textual/widgets/__init__.py +58 -0
  198. ynab_cli/host/textual/widgets/categories.py +90 -0
  199. ynab_cli/host/textual/widgets/payees.py +154 -0
  200. ynab_cli/host/textual/widgets/transactions.py +89 -0
  201. ynab_cli/py.typed +0 -0
  202. ynab_cli-0.3.0.dist-info/METADATA +102 -0
  203. ynab_cli-0.3.0.dist-info/RECORD +206 -0
  204. ynab_cli-0.3.0.dist-info/WHEEL +4 -0
  205. ynab_cli-0.3.0.dist-info/entry_points.txt +2 -0
  206. ynab_cli-0.3.0.dist-info/licenses/LICENSE.md +21 -0
@@ -0,0 +1,268 @@
1
+ import ssl
2
+ from typing import Any
3
+
4
+ import httpx
5
+ from attrs import define, evolve, field
6
+
7
+
8
+ @define
9
+ class Client:
10
+ """A class for keeping track of data related to the API
11
+
12
+ The following are accepted as keyword arguments and will be used to construct httpx Clients internally:
13
+
14
+ ``base_url``: The base URL for the API, all requests are made to a relative path to this URL
15
+
16
+ ``cookies``: A dictionary of cookies to be sent with every request
17
+
18
+ ``headers``: A dictionary of headers to be sent with every request
19
+
20
+ ``timeout``: The maximum amount of a time a request can take. API functions will raise
21
+ httpx.TimeoutException if this is exceeded.
22
+
23
+ ``verify_ssl``: Whether or not to verify the SSL certificate of the API server. This should be True in production,
24
+ but can be set to False for testing purposes.
25
+
26
+ ``follow_redirects``: Whether or not to follow redirects. Default value is False.
27
+
28
+ ``httpx_args``: A dictionary of additional arguments to be passed to the ``httpx.Client`` and ``httpx.AsyncClient`` constructor.
29
+
30
+
31
+ Attributes:
32
+ raise_on_unexpected_status: Whether or not to raise an errors.UnexpectedStatusError if the API returns a
33
+ status code that was not documented in the source OpenAPI document. Can also be provided as a keyword
34
+ argument to the constructor.
35
+ """
36
+
37
+ raise_on_unexpected_status: bool = field(default=False, kw_only=True)
38
+ _base_url: str = field(alias="base_url")
39
+ _cookies: dict[str, str] = field(factory=dict, kw_only=True, alias="cookies")
40
+ _headers: dict[str, str] = field(factory=dict, kw_only=True, alias="headers")
41
+ _timeout: httpx.Timeout | None = field(default=None, kw_only=True, alias="timeout")
42
+ _verify_ssl: str | bool | ssl.SSLContext = field(default=True, kw_only=True, alias="verify_ssl")
43
+ _follow_redirects: bool = field(default=False, kw_only=True, alias="follow_redirects")
44
+ _httpx_args: dict[str, Any] = field(factory=dict, kw_only=True, alias="httpx_args")
45
+ _client: httpx.Client | None = field(default=None, init=False)
46
+ _async_client: httpx.AsyncClient | None = field(default=None, init=False)
47
+
48
+ def with_headers(self, headers: dict[str, str]) -> "Client":
49
+ """Get a new client matching this one with additional headers"""
50
+ if self._client is not None:
51
+ self._client.headers.update(headers)
52
+ if self._async_client is not None:
53
+ self._async_client.headers.update(headers)
54
+ return evolve(self, headers={**self._headers, **headers})
55
+
56
+ def with_cookies(self, cookies: dict[str, str]) -> "Client":
57
+ """Get a new client matching this one with additional cookies"""
58
+ if self._client is not None:
59
+ self._client.cookies.update(cookies)
60
+ if self._async_client is not None:
61
+ self._async_client.cookies.update(cookies)
62
+ return evolve(self, cookies={**self._cookies, **cookies})
63
+
64
+ def with_timeout(self, timeout: httpx.Timeout) -> "Client":
65
+ """Get a new client matching this one with a new timeout (in seconds)"""
66
+ if self._client is not None:
67
+ self._client.timeout = timeout
68
+ if self._async_client is not None:
69
+ self._async_client.timeout = timeout
70
+ return evolve(self, timeout=timeout)
71
+
72
+ def set_httpx_client(self, client: httpx.Client) -> "Client":
73
+ """Manually set the underlying httpx.Client
74
+
75
+ **NOTE**: This will override any other settings on the client, including cookies, headers, and timeout.
76
+ """
77
+ self._client = client
78
+ return self
79
+
80
+ def get_httpx_client(self) -> httpx.Client:
81
+ """Get the underlying httpx.Client, constructing a new one if not previously set"""
82
+ if self._client is None:
83
+ self._client = httpx.Client(
84
+ base_url=self._base_url,
85
+ cookies=self._cookies,
86
+ headers=self._headers,
87
+ timeout=self._timeout,
88
+ verify=self._verify_ssl,
89
+ follow_redirects=self._follow_redirects,
90
+ **self._httpx_args,
91
+ )
92
+ return self._client
93
+
94
+ def __enter__(self) -> "Client":
95
+ """Enter a context manager for self.client—you cannot enter twice (see httpx docs)"""
96
+ self.get_httpx_client().__enter__()
97
+ return self
98
+
99
+ def __exit__(self, *args: Any, **kwargs: Any) -> None:
100
+ """Exit a context manager for internal httpx.Client (see httpx docs)"""
101
+ self.get_httpx_client().__exit__(*args, **kwargs)
102
+
103
+ def set_async_httpx_client(self, async_client: httpx.AsyncClient) -> "Client":
104
+ """Manually the underlying httpx.AsyncClient
105
+
106
+ **NOTE**: This will override any other settings on the client, including cookies, headers, and timeout.
107
+ """
108
+ self._async_client = async_client
109
+ return self
110
+
111
+ def get_async_httpx_client(self) -> httpx.AsyncClient:
112
+ """Get the underlying httpx.AsyncClient, constructing a new one if not previously set"""
113
+ if self._async_client is None:
114
+ self._async_client = httpx.AsyncClient(
115
+ base_url=self._base_url,
116
+ cookies=self._cookies,
117
+ headers=self._headers,
118
+ timeout=self._timeout,
119
+ verify=self._verify_ssl,
120
+ follow_redirects=self._follow_redirects,
121
+ **self._httpx_args,
122
+ )
123
+ return self._async_client
124
+
125
+ async def __aenter__(self) -> "Client":
126
+ """Enter a context manager for underlying httpx.AsyncClient—you cannot enter twice (see httpx docs)"""
127
+ await self.get_async_httpx_client().__aenter__()
128
+ return self
129
+
130
+ async def __aexit__(self, *args: Any, **kwargs: Any) -> None:
131
+ """Exit a context manager for underlying httpx.AsyncClient (see httpx docs)"""
132
+ await self.get_async_httpx_client().__aexit__(*args, **kwargs)
133
+
134
+
135
+ @define
136
+ class AuthenticatedClient:
137
+ """A Client which has been authenticated for use on secured endpoints
138
+
139
+ The following are accepted as keyword arguments and will be used to construct httpx Clients internally:
140
+
141
+ ``base_url``: The base URL for the API, all requests are made to a relative path to this URL
142
+
143
+ ``cookies``: A dictionary of cookies to be sent with every request
144
+
145
+ ``headers``: A dictionary of headers to be sent with every request
146
+
147
+ ``timeout``: The maximum amount of a time a request can take. API functions will raise
148
+ httpx.TimeoutException if this is exceeded.
149
+
150
+ ``verify_ssl``: Whether or not to verify the SSL certificate of the API server. This should be True in production,
151
+ but can be set to False for testing purposes.
152
+
153
+ ``follow_redirects``: Whether or not to follow redirects. Default value is False.
154
+
155
+ ``httpx_args``: A dictionary of additional arguments to be passed to the ``httpx.Client`` and ``httpx.AsyncClient`` constructor.
156
+
157
+
158
+ Attributes:
159
+ raise_on_unexpected_status: Whether or not to raise an errors.UnexpectedStatusError if the API returns a
160
+ status code that was not documented in the source OpenAPI document. Can also be provided as a keyword
161
+ argument to the constructor.
162
+ token: The token to use for authentication
163
+ prefix: The prefix to use for the Authorization header
164
+ auth_header_name: The name of the Authorization header
165
+ """
166
+
167
+ raise_on_unexpected_status: bool = field(default=False, kw_only=True)
168
+ _base_url: str = field(alias="base_url")
169
+ _cookies: dict[str, str] = field(factory=dict, kw_only=True, alias="cookies")
170
+ _headers: dict[str, str] = field(factory=dict, kw_only=True, alias="headers")
171
+ _timeout: httpx.Timeout | None = field(default=None, kw_only=True, alias="timeout")
172
+ _verify_ssl: str | bool | ssl.SSLContext = field(default=True, kw_only=True, alias="verify_ssl")
173
+ _follow_redirects: bool = field(default=False, kw_only=True, alias="follow_redirects")
174
+ _httpx_args: dict[str, Any] = field(factory=dict, kw_only=True, alias="httpx_args")
175
+ _client: httpx.Client | None = field(default=None, init=False)
176
+ _async_client: httpx.AsyncClient | None = field(default=None, init=False)
177
+
178
+ token: str
179
+ prefix: str = "Bearer"
180
+ auth_header_name: str = "Authorization"
181
+
182
+ def with_headers(self, headers: dict[str, str]) -> "AuthenticatedClient":
183
+ """Get a new client matching this one with additional headers"""
184
+ if self._client is not None:
185
+ self._client.headers.update(headers)
186
+ if self._async_client is not None:
187
+ self._async_client.headers.update(headers)
188
+ return evolve(self, headers={**self._headers, **headers})
189
+
190
+ def with_cookies(self, cookies: dict[str, str]) -> "AuthenticatedClient":
191
+ """Get a new client matching this one with additional cookies"""
192
+ if self._client is not None:
193
+ self._client.cookies.update(cookies)
194
+ if self._async_client is not None:
195
+ self._async_client.cookies.update(cookies)
196
+ return evolve(self, cookies={**self._cookies, **cookies})
197
+
198
+ def with_timeout(self, timeout: httpx.Timeout) -> "AuthenticatedClient":
199
+ """Get a new client matching this one with a new timeout (in seconds)"""
200
+ if self._client is not None:
201
+ self._client.timeout = timeout
202
+ if self._async_client is not None:
203
+ self._async_client.timeout = timeout
204
+ return evolve(self, timeout=timeout)
205
+
206
+ def set_httpx_client(self, client: httpx.Client) -> "AuthenticatedClient":
207
+ """Manually set the underlying httpx.Client
208
+
209
+ **NOTE**: This will override any other settings on the client, including cookies, headers, and timeout.
210
+ """
211
+ self._client = client
212
+ return self
213
+
214
+ def get_httpx_client(self) -> httpx.Client:
215
+ """Get the underlying httpx.Client, constructing a new one if not previously set"""
216
+ if self._client is None:
217
+ self._headers[self.auth_header_name] = f"{self.prefix} {self.token}" if self.prefix else self.token
218
+ self._client = httpx.Client(
219
+ base_url=self._base_url,
220
+ cookies=self._cookies,
221
+ headers=self._headers,
222
+ timeout=self._timeout,
223
+ verify=self._verify_ssl,
224
+ follow_redirects=self._follow_redirects,
225
+ **self._httpx_args,
226
+ )
227
+ return self._client
228
+
229
+ def __enter__(self) -> "AuthenticatedClient":
230
+ """Enter a context manager for self.client—you cannot enter twice (see httpx docs)"""
231
+ self.get_httpx_client().__enter__()
232
+ return self
233
+
234
+ def __exit__(self, *args: Any, **kwargs: Any) -> None:
235
+ """Exit a context manager for internal httpx.Client (see httpx docs)"""
236
+ self.get_httpx_client().__exit__(*args, **kwargs)
237
+
238
+ def set_async_httpx_client(self, async_client: httpx.AsyncClient) -> "AuthenticatedClient":
239
+ """Manually the underlying httpx.AsyncClient
240
+
241
+ **NOTE**: This will override any other settings on the client, including cookies, headers, and timeout.
242
+ """
243
+ self._async_client = async_client
244
+ return self
245
+
246
+ def get_async_httpx_client(self) -> httpx.AsyncClient:
247
+ """Get the underlying httpx.AsyncClient, constructing a new one if not previously set"""
248
+ if self._async_client is None:
249
+ self._headers[self.auth_header_name] = f"{self.prefix} {self.token}" if self.prefix else self.token
250
+ self._async_client = httpx.AsyncClient(
251
+ base_url=self._base_url,
252
+ cookies=self._cookies,
253
+ headers=self._headers,
254
+ timeout=self._timeout,
255
+ verify=self._verify_ssl,
256
+ follow_redirects=self._follow_redirects,
257
+ **self._httpx_args,
258
+ )
259
+ return self._async_client
260
+
261
+ async def __aenter__(self) -> "AuthenticatedClient":
262
+ """Enter a context manager for underlying httpx.AsyncClient—you cannot enter twice (see httpx docs)"""
263
+ await self.get_async_httpx_client().__aenter__()
264
+ return self
265
+
266
+ async def __aexit__(self, *args: Any, **kwargs: Any) -> None:
267
+ """Exit a context manager for underlying httpx.AsyncClient (see httpx docs)"""
268
+ await self.get_async_httpx_client().__aexit__(*args, **kwargs)
@@ -0,0 +1,16 @@
1
+ """Contains shared errors types that can be raised from API functions"""
2
+
3
+
4
+ class UnexpectedStatusError(Exception):
5
+ """Raised by api functions when the response status an undocumented status and Client.raise_on_unexpected_status is True"""
6
+
7
+ def __init__(self, status_code: int, content: bytes):
8
+ self.status_code = status_code
9
+ self.content = content
10
+
11
+ super().__init__(
12
+ f"Unexpected status code: {status_code}\n\nResponse content:\n{content.decode(errors='ignore')}"
13
+ )
14
+
15
+
16
+ __all__ = ["UnexpectedStatusError"]
@@ -0,0 +1,229 @@
1
+ """Contains all the data models used in inputs/outputs"""
2
+
3
+ from .account import Account
4
+ from .account_response import AccountResponse
5
+ from .account_response_data import AccountResponseData
6
+ from .account_type import AccountType
7
+ from .accounts_response import AccountsResponse
8
+ from .accounts_response_data import AccountsResponseData
9
+ from .budget_detail import BudgetDetail
10
+ from .budget_detail_response import BudgetDetailResponse
11
+ from .budget_detail_response_data import BudgetDetailResponseData
12
+ from .budget_settings import BudgetSettings
13
+ from .budget_settings_response import BudgetSettingsResponse
14
+ from .budget_settings_response_data import BudgetSettingsResponseData
15
+ from .budget_summary import BudgetSummary
16
+ from .budget_summary_response import BudgetSummaryResponse
17
+ from .budget_summary_response_data import BudgetSummaryResponseData
18
+ from .bulk_response import BulkResponse
19
+ from .bulk_response_data import BulkResponseData
20
+ from .bulk_response_data_bulk import BulkResponseDataBulk
21
+ from .bulk_transactions import BulkTransactions
22
+ from .categories_response import CategoriesResponse
23
+ from .categories_response_data import CategoriesResponseData
24
+ from .category import Category
25
+ from .category_goal_type_type_1 import CategoryGoalTypeType1
26
+ from .category_goal_type_type_2_type_1 import CategoryGoalTypeType2Type1
27
+ from .category_goal_type_type_3_type_1 import CategoryGoalTypeType3Type1
28
+ from .category_group import CategoryGroup
29
+ from .category_group_with_categories import CategoryGroupWithCategories
30
+ from .category_response import CategoryResponse
31
+ from .category_response_data import CategoryResponseData
32
+ from .currency_format_type_0 import CurrencyFormatType0
33
+ from .date_format_type_0 import DateFormatType0
34
+ from .error_detail import ErrorDetail
35
+ from .error_response import ErrorResponse
36
+ from .existing_transaction import ExistingTransaction
37
+ from .get_transactions_by_account_type import GetTransactionsByAccountType
38
+ from .get_transactions_by_category_type import GetTransactionsByCategoryType
39
+ from .get_transactions_by_month_type import GetTransactionsByMonthType
40
+ from .get_transactions_by_payee_type import GetTransactionsByPayeeType
41
+ from .get_transactions_type import GetTransactionsType
42
+ from .hybrid_transaction import HybridTransaction
43
+ from .hybrid_transaction_type import HybridTransactionType
44
+ from .hybrid_transactions_response import HybridTransactionsResponse
45
+ from .hybrid_transactions_response_data import HybridTransactionsResponseData
46
+ from .loan_account_periodic_value_type_0 import LoanAccountPeriodicValueType0
47
+ from .month_detail import MonthDetail
48
+ from .month_detail_response import MonthDetailResponse
49
+ from .month_detail_response_data import MonthDetailResponseData
50
+ from .month_summaries_response import MonthSummariesResponse
51
+ from .month_summaries_response_data import MonthSummariesResponseData
52
+ from .month_summary import MonthSummary
53
+ from .new_transaction import NewTransaction
54
+ from .patch_category_wrapper import PatchCategoryWrapper
55
+ from .patch_month_category_wrapper import PatchMonthCategoryWrapper
56
+ from .patch_payee_wrapper import PatchPayeeWrapper
57
+ from .patch_transactions_wrapper import PatchTransactionsWrapper
58
+ from .payee import Payee
59
+ from .payee_location import PayeeLocation
60
+ from .payee_location_response import PayeeLocationResponse
61
+ from .payee_location_response_data import PayeeLocationResponseData
62
+ from .payee_locations_response import PayeeLocationsResponse
63
+ from .payee_locations_response_data import PayeeLocationsResponseData
64
+ from .payee_response import PayeeResponse
65
+ from .payee_response_data import PayeeResponseData
66
+ from .payees_response import PayeesResponse
67
+ from .payees_response_data import PayeesResponseData
68
+ from .post_account_wrapper import PostAccountWrapper
69
+ from .post_scheduled_transaction_wrapper import PostScheduledTransactionWrapper
70
+ from .post_transactions_wrapper import PostTransactionsWrapper
71
+ from .put_scheduled_transaction_wrapper import PutScheduledTransactionWrapper
72
+ from .put_transaction_wrapper import PutTransactionWrapper
73
+ from .save_account import SaveAccount
74
+ from .save_category import SaveCategory
75
+ from .save_category_response import SaveCategoryResponse
76
+ from .save_category_response_data import SaveCategoryResponseData
77
+ from .save_month_category import SaveMonthCategory
78
+ from .save_payee import SavePayee
79
+ from .save_payee_response import SavePayeeResponse
80
+ from .save_payee_response_data import SavePayeeResponseData
81
+ from .save_scheduled_transaction import SaveScheduledTransaction
82
+ from .save_sub_transaction import SaveSubTransaction
83
+ from .save_transaction_with_id_or_import_id import SaveTransactionWithIdOrImportId
84
+ from .save_transaction_with_optional_fields import SaveTransactionWithOptionalFields
85
+ from .save_transactions_response import SaveTransactionsResponse
86
+ from .save_transactions_response_data import SaveTransactionsResponseData
87
+ from .scheduled_sub_transaction import ScheduledSubTransaction
88
+ from .scheduled_transaction_detail import ScheduledTransactionDetail
89
+ from .scheduled_transaction_frequency import ScheduledTransactionFrequency
90
+ from .scheduled_transaction_response import ScheduledTransactionResponse
91
+ from .scheduled_transaction_response_data import ScheduledTransactionResponseData
92
+ from .scheduled_transaction_summary import ScheduledTransactionSummary
93
+ from .scheduled_transaction_summary_frequency import ScheduledTransactionSummaryFrequency
94
+ from .scheduled_transactions_response import ScheduledTransactionsResponse
95
+ from .scheduled_transactions_response_data import ScheduledTransactionsResponseData
96
+ from .sub_transaction import SubTransaction
97
+ from .transaction_cleared_status import TransactionClearedStatus
98
+ from .transaction_detail import TransactionDetail
99
+ from .transaction_flag_color_type_1 import TransactionFlagColorType1
100
+ from .transaction_flag_color_type_2_type_1 import TransactionFlagColorType2Type1
101
+ from .transaction_flag_color_type_3_type_1 import TransactionFlagColorType3Type1
102
+ from .transaction_response import TransactionResponse
103
+ from .transaction_response_data import TransactionResponseData
104
+ from .transaction_summary import TransactionSummary
105
+ from .transaction_summary_debt_transaction_type_type_1 import TransactionSummaryDebtTransactionTypeType1
106
+ from .transaction_summary_debt_transaction_type_type_2_type_1 import TransactionSummaryDebtTransactionTypeType2Type1
107
+ from .transaction_summary_debt_transaction_type_type_3_type_1 import TransactionSummaryDebtTransactionTypeType3Type1
108
+ from .transactions_import_response import TransactionsImportResponse
109
+ from .transactions_import_response_data import TransactionsImportResponseData
110
+ from .transactions_response import TransactionsResponse
111
+ from .transactions_response_data import TransactionsResponseData
112
+ from .user import User
113
+ from .user_response import UserResponse
114
+ from .user_response_data import UserResponseData
115
+
116
+ __all__ = (
117
+ "Account",
118
+ "AccountResponse",
119
+ "AccountResponseData",
120
+ "AccountType",
121
+ "AccountsResponse",
122
+ "AccountsResponseData",
123
+ "BudgetDetail",
124
+ "BudgetDetailResponse",
125
+ "BudgetDetailResponseData",
126
+ "BudgetSettings",
127
+ "BudgetSettingsResponse",
128
+ "BudgetSettingsResponseData",
129
+ "BudgetSummary",
130
+ "BudgetSummaryResponse",
131
+ "BudgetSummaryResponseData",
132
+ "BulkResponse",
133
+ "BulkResponseData",
134
+ "BulkResponseDataBulk",
135
+ "BulkTransactions",
136
+ "CategoriesResponse",
137
+ "CategoriesResponseData",
138
+ "Category",
139
+ "CategoryGoalTypeType1",
140
+ "CategoryGoalTypeType2Type1",
141
+ "CategoryGoalTypeType3Type1",
142
+ "CategoryGroup",
143
+ "CategoryGroupWithCategories",
144
+ "CategoryResponse",
145
+ "CategoryResponseData",
146
+ "CurrencyFormatType0",
147
+ "DateFormatType0",
148
+ "ErrorDetail",
149
+ "ErrorResponse",
150
+ "ExistingTransaction",
151
+ "GetTransactionsByAccountType",
152
+ "GetTransactionsByCategoryType",
153
+ "GetTransactionsByMonthType",
154
+ "GetTransactionsByPayeeType",
155
+ "GetTransactionsType",
156
+ "HybridTransaction",
157
+ "HybridTransactionType",
158
+ "HybridTransactionsResponse",
159
+ "HybridTransactionsResponseData",
160
+ "LoanAccountPeriodicValueType0",
161
+ "MonthDetail",
162
+ "MonthDetailResponse",
163
+ "MonthDetailResponseData",
164
+ "MonthSummariesResponse",
165
+ "MonthSummariesResponseData",
166
+ "MonthSummary",
167
+ "NewTransaction",
168
+ "PatchCategoryWrapper",
169
+ "PatchMonthCategoryWrapper",
170
+ "PatchPayeeWrapper",
171
+ "PatchTransactionsWrapper",
172
+ "Payee",
173
+ "PayeeLocation",
174
+ "PayeeLocationResponse",
175
+ "PayeeLocationResponseData",
176
+ "PayeeLocationsResponse",
177
+ "PayeeLocationsResponseData",
178
+ "PayeeResponse",
179
+ "PayeeResponseData",
180
+ "PayeesResponse",
181
+ "PayeesResponseData",
182
+ "PostAccountWrapper",
183
+ "PostScheduledTransactionWrapper",
184
+ "PostTransactionsWrapper",
185
+ "PutScheduledTransactionWrapper",
186
+ "PutTransactionWrapper",
187
+ "SaveAccount",
188
+ "SaveCategory",
189
+ "SaveCategoryResponse",
190
+ "SaveCategoryResponseData",
191
+ "SaveMonthCategory",
192
+ "SavePayee",
193
+ "SavePayeeResponse",
194
+ "SavePayeeResponseData",
195
+ "SaveScheduledTransaction",
196
+ "SaveSubTransaction",
197
+ "SaveTransactionWithIdOrImportId",
198
+ "SaveTransactionWithOptionalFields",
199
+ "SaveTransactionsResponse",
200
+ "SaveTransactionsResponseData",
201
+ "ScheduledSubTransaction",
202
+ "ScheduledTransactionDetail",
203
+ "ScheduledTransactionFrequency",
204
+ "ScheduledTransactionResponse",
205
+ "ScheduledTransactionResponseData",
206
+ "ScheduledTransactionSummary",
207
+ "ScheduledTransactionSummaryFrequency",
208
+ "ScheduledTransactionsResponse",
209
+ "ScheduledTransactionsResponseData",
210
+ "SubTransaction",
211
+ "TransactionClearedStatus",
212
+ "TransactionDetail",
213
+ "TransactionFlagColorType1",
214
+ "TransactionFlagColorType2Type1",
215
+ "TransactionFlagColorType3Type1",
216
+ "TransactionResponse",
217
+ "TransactionResponseData",
218
+ "TransactionSummary",
219
+ "TransactionSummaryDebtTransactionTypeType1",
220
+ "TransactionSummaryDebtTransactionTypeType2Type1",
221
+ "TransactionSummaryDebtTransactionTypeType3Type1",
222
+ "TransactionsImportResponse",
223
+ "TransactionsImportResponseData",
224
+ "TransactionsResponse",
225
+ "TransactionsResponseData",
226
+ "User",
227
+ "UserResponse",
228
+ "UserResponseData",
229
+ )