python3-capsolver 1.1.1__tar.gz → 1.2.1__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.
Files changed (50) hide show
  1. {python3_capsolver-1.1.1/src/python3_capsolver.egg-info → python3_capsolver-1.2.1}/PKG-INFO +109 -8
  2. python3_capsolver-1.2.1/README.md +162 -0
  3. {python3_capsolver-1.1.1 → python3_capsolver-1.2.1}/pyproject.toml +16 -1
  4. python3_capsolver-1.2.1/src/python3_capsolver/__version__.py +1 -0
  5. {python3_capsolver-1.1.1 → python3_capsolver-1.2.1}/src/python3_capsolver/core/aio_captcha_instrument.py +9 -2
  6. {python3_capsolver-1.1.1 → python3_capsolver-1.2.1}/src/python3_capsolver/core/captcha_instrument.py +4 -1
  7. {python3_capsolver-1.1.1 → python3_capsolver-1.2.1}/src/python3_capsolver/core/serializer.py +10 -4
  8. {python3_capsolver-1.1.1 → python3_capsolver-1.2.1}/src/python3_capsolver/core/sio_captcha_instrument.py +9 -1
  9. {python3_capsolver-1.1.1 → python3_capsolver-1.2.1}/src/python3_capsolver/image_to_text.py +6 -1
  10. {python3_capsolver-1.1.1 → python3_capsolver-1.2.1/src/python3_capsolver.egg-info}/PKG-INFO +109 -8
  11. {python3_capsolver-1.1.1 → python3_capsolver-1.2.1}/src/python3_capsolver.egg-info/requires.txt +1 -1
  12. python3_capsolver-1.2.1/tests/test_control.py +175 -0
  13. python3_capsolver-1.1.1/README.md +0 -61
  14. python3_capsolver-1.1.1/src/python3_capsolver/__version__.py +0 -1
  15. python3_capsolver-1.1.1/tests/test_control.py +0 -38
  16. {python3_capsolver-1.1.1 → python3_capsolver-1.2.1}/LICENSE +0 -0
  17. {python3_capsolver-1.1.1 → python3_capsolver-1.2.1}/MANIFEST.in +0 -0
  18. {python3_capsolver-1.1.1 → python3_capsolver-1.2.1}/setup.cfg +0 -0
  19. {python3_capsolver-1.1.1 → python3_capsolver-1.2.1}/src/python3_capsolver/__init__.py +0 -0
  20. {python3_capsolver-1.1.1 → python3_capsolver-1.2.1}/src/python3_capsolver/aws_waf.py +0 -0
  21. {python3_capsolver-1.1.1 → python3_capsolver-1.2.1}/src/python3_capsolver/cloudflare.py +0 -0
  22. {python3_capsolver-1.1.1 → python3_capsolver-1.2.1}/src/python3_capsolver/control.py +0 -0
  23. {python3_capsolver-1.1.1 → python3_capsolver-1.2.1}/src/python3_capsolver/core/__init__.py +0 -0
  24. {python3_capsolver-1.1.1 → python3_capsolver-1.2.1}/src/python3_capsolver/core/base.py +0 -0
  25. {python3_capsolver-1.1.1 → python3_capsolver-1.2.1}/src/python3_capsolver/core/const.py +0 -0
  26. {python3_capsolver-1.1.1 → python3_capsolver-1.2.1}/src/python3_capsolver/core/context_instr.py +0 -0
  27. {python3_capsolver-1.1.1 → python3_capsolver-1.2.1}/src/python3_capsolver/core/enum.py +0 -0
  28. {python3_capsolver-1.1.1 → python3_capsolver-1.2.1}/src/python3_capsolver/core/utils.py +0 -0
  29. {python3_capsolver-1.1.1 → python3_capsolver-1.2.1}/src/python3_capsolver/datadome_slider.py +0 -0
  30. {python3_capsolver-1.1.1 → python3_capsolver-1.2.1}/src/python3_capsolver/friendly_captcha.py +0 -0
  31. {python3_capsolver-1.1.1 → python3_capsolver-1.2.1}/src/python3_capsolver/gee_test.py +0 -0
  32. {python3_capsolver-1.1.1 → python3_capsolver-1.2.1}/src/python3_capsolver/mt_captcha.py +0 -0
  33. {python3_capsolver-1.1.1 → python3_capsolver-1.2.1}/src/python3_capsolver/recaptcha.py +0 -0
  34. {python3_capsolver-1.1.1 → python3_capsolver-1.2.1}/src/python3_capsolver/vision_engine.py +0 -0
  35. {python3_capsolver-1.1.1 → python3_capsolver-1.2.1}/src/python3_capsolver/yandex.py +0 -0
  36. {python3_capsolver-1.1.1 → python3_capsolver-1.2.1}/src/python3_capsolver.egg-info/SOURCES.txt +0 -0
  37. {python3_capsolver-1.1.1 → python3_capsolver-1.2.1}/src/python3_capsolver.egg-info/dependency_links.txt +0 -0
  38. {python3_capsolver-1.1.1 → python3_capsolver-1.2.1}/src/python3_capsolver.egg-info/top_level.txt +0 -0
  39. {python3_capsolver-1.1.1 → python3_capsolver-1.2.1}/tests/test_aws_waf.py +0 -0
  40. {python3_capsolver-1.1.1 → python3_capsolver-1.2.1}/tests/test_cloudflare.py +0 -0
  41. {python3_capsolver-1.1.1 → python3_capsolver-1.2.1}/tests/test_core.py +0 -0
  42. {python3_capsolver-1.1.1 → python3_capsolver-1.2.1}/tests/test_datadome.py +0 -0
  43. {python3_capsolver-1.1.1 → python3_capsolver-1.2.1}/tests/test_friendly.py +0 -0
  44. {python3_capsolver-1.1.1 → python3_capsolver-1.2.1}/tests/test_gee_test.py +0 -0
  45. {python3_capsolver-1.1.1 → python3_capsolver-1.2.1}/tests/test_image_to_text.py +0 -0
  46. {python3_capsolver-1.1.1 → python3_capsolver-1.2.1}/tests/test_instrument.py +0 -0
  47. {python3_capsolver-1.1.1 → python3_capsolver-1.2.1}/tests/test_mt_captcha.py +0 -0
  48. {python3_capsolver-1.1.1 → python3_capsolver-1.2.1}/tests/test_recaptcha.py +0 -0
  49. {python3_capsolver-1.1.1 → python3_capsolver-1.2.1}/tests/test_vision_engine.py +0 -0
  50. {python3_capsolver-1.1.1 → python3_capsolver-1.2.1}/tests/test_yandex.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python3-capsolver
3
- Version: 1.1.1
3
+ Version: 1.2.1
4
4
  Summary: Python 3.8+ Capsolver library with AIO module.
5
5
  Author-email: AndreiDrang <python-captcha@pm.me>
6
6
  License: MIT License
@@ -30,7 +30,7 @@ Description-Content-Type: text/markdown
30
30
  License-File: LICENSE
31
31
  Requires-Dist: requests>=2.21.0
32
32
  Requires-Dist: aiohttp>=3.9.2
33
- Requires-Dist: msgspec<=0.21,>=0.18
33
+ Requires-Dist: msgspec>=0.18
34
34
  Requires-Dist: tenacity<10,>=8
35
35
  Dynamic: license-file
36
36
 
@@ -61,11 +61,31 @@ Tested on UNIX based OS.
61
61
 
62
62
  The library is intended for software developers and is used to work with the [Capsolver](https://dashboard.capsolver.com/passport/register?inviteCode=kQTn-tG07Jb1) service API.
63
63
 
64
+ ## Features
65
+ - **Modern Tooling**: Uses `uv` for fast, reliable dependency management and environment isolation.
66
+ - **Sync & Async Support**: Full support for both synchronous (`requests`) and asynchronous (`aiohttp`) operations.
67
+ - **Type Safety**: Enums for captcha types and response statuses.
68
+ - **Resilience**: Built-in retries using `tenacity`.
69
+ - **Performance**: High-speed JSON serialization with `msgspec`.
70
+ - **Coverage**: Supports ReCaptcha (V2/V3), Cloudflare, DataDome, GeeTest, MtCaptcha, AWS WAF, Yandex, and ImageToText.
71
+
64
72
  ## How to install?
65
73
 
66
- We recommend using the latest version of Python. `python3-capsolver` supports Python 3.7+.
74
+ We recommend using the latest version of Python. `python3-capsolver` supports Python 3.8+.
75
+
76
+ ### Development (using uv)
67
77
 
68
- ### pip
78
+ This project uses [uv](https://github.com/astral-sh/uv) for fast, reliable dependency management.
79
+
80
+ ```bash
81
+ # Install uv (if not already installed)
82
+ curl -LsSf https://astral.sh/uv/install.sh | sh
83
+
84
+ # Install project dependencies
85
+ uv sync --all-groups
86
+ ```
87
+
88
+ ### Production (pip)
69
89
 
70
90
  ```bash
71
91
  pip install python3-capsolver
@@ -73,18 +93,99 @@ pip install python3-capsolver
73
93
 
74
94
  ## How to use?
75
95
 
76
- Is described in the [documentation-website](https://andreidrang.github.io/python3-capsolver/).
96
+ Detailed documentation is available on the [website](https://andreidrang.github.io/python3-capsolver/).
97
+
98
+ ### Quick Start
99
+
100
+ #### Synchronous Example (ImageToText)
101
+ ```python
102
+ from python3_capsolver.image_to_text import ImageToText
77
103
 
104
+ # 1. Initialize with API Key
105
+ solver = ImageToText(api_key="YOUR_API_KEY")
106
+
107
+ # 2. Solve
108
+ result = solver.captcha_handler(
109
+ task_payload={
110
+ "body": "base64_encoded_image_string"
111
+ }
112
+ )
113
+
114
+ # 3. Check result
115
+ if result["errorId"] == 0:
116
+ print("Solution:", result["solution"])
117
+ else:
118
+ print("Error:", result["errorCode"])
119
+ ```
120
+
121
+ #### Asynchronous Example (ReCaptcha)
122
+ ```python
123
+ import asyncio
124
+ from python3_capsolver.recaptcha import ReCaptcha
125
+ from python3_capsolver.core.enum import CaptchaTypeEnm
126
+
127
+ async def main():
128
+ # 1. Initialize
129
+ solver = ReCaptcha(
130
+ api_key="YOUR_API_KEY",
131
+ captcha_type=CaptchaTypeEnm.ReCaptchaV2TaskProxyLess
132
+ )
133
+
134
+ # 2. Solve
135
+ result = await solver.aio_captcha_handler(
136
+ task_payload={
137
+ "websiteURL": "https://example.com",
138
+ "websiteKey": "SITE_KEY"
139
+ }
140
+ )
141
+
142
+ print(result)
143
+
144
+ if __name__ == "__main__":
145
+ asyncio.run(main())
146
+ ```
147
+
148
+ ## Supported Captcha Types
149
+ - **ReCaptcha**: V2 (Task/Enterprise), V3 (Task/Enterprise)
150
+ - **HCaptcha**: Task, Enterprise
151
+ - **Cloudflare**: Turnstile
152
+ - **GeeTest**: V3, V4
153
+ - **DataDome**: Slider
154
+ - **MtCaptcha**
155
+ - **AWS WAF**
156
+ - **Yandex SmartCaptcha**
157
+ - **ImageToText**: General image CAPTCHAs
158
+
159
+ ## Documentation & Context (For LLMs)
160
+ - **Project Structure**: See `AGENTS.md` in root and subdirectories for internal architecture.
161
+ - **Entry Points**: `src/python3_capsolver/*.py` contains service-specific classes (e.g., `ReCaptcha`, `HCaptcha`).
162
+ - **Core Logic**: `src/python3_capsolver/core/base.py` handles the API communication loop.
163
+ - **Enums**: Use `python3_capsolver.core.enum` for type-safe parameters.
78
164
 
79
165
  ## How to test?
80
166
 
81
- 1. You need set ``API_KEY`` in your environment(get this value from you account).
82
- 2. Run command ``make tests``, from root directory.
167
+ The project uses `uv` for testing and development tasks.
168
+
169
+ ```bash
170
+ # 1. Set API_KEY in your environment (get this value from your account)
171
+ export API_KEY="your_api_key_here"
172
+
173
+ # 2. Run tests (uses uv internally)
174
+ make tests
175
+
176
+ # Other useful make targets:
177
+ # make install # Install dependencies with uv sync
178
+ # make lint # Run linting checks
179
+ # make build # Build the package
180
+ # make doc # Generate documentation
181
+ ```
182
+
183
+ All `make` commands automatically use `uv` to run commands in the isolated environment.
83
184
 
84
185
 
85
186
  ### Changelog
86
187
 
87
- Check [releases page](https://github.com/AndreiDrang/python3-capsolver/releases).
188
+ See [CHANGELOG.md](https://github.com/AndreiDrang/python3-capsolver/blob/main/CHANGELOG.md) for version history and detailed changes.
88
189
 
89
190
  ### How to get API Key to work with the library
90
191
  1. On the page - https://dashboard.capsolver.com/overview/user-center
@@ -0,0 +1,162 @@
1
+ # python3-capsolver
2
+
3
+ [![Capsolver.png](https://s.vyjava.xyz/files/2024/12-December/17/109278aa/Capsolver.png)](https://vyjava.xyz/dashboard/image/109278aa-961a-4503-bed0-0a9c838dcef2)
4
+
5
+ <hr>
6
+
7
+ [![PyPI version](https://badge.fury.io/py/python3-capsolver.svg)](https://badge.fury.io/py/python3-capsolver)
8
+ [![Python versions](https://img.shields.io/pypi/pyversions/python3-capsolver.svg?logo=python&logoColor=FBE072)](https://badge.fury.io/py/python3-capsolver)
9
+ [![Downloads](https://static.pepy.tech/badge/python3-capsolver/month)](https://pepy.tech/project/python3-capsolver)
10
+ [![Static Badge](https://img.shields.io/badge/docs-Sphinx-green?label=Documentation&labelColor=gray)](https://andreidrang.github.io/python3-capsolver/)
11
+
12
+ [![Maintainability](https://api.codeclimate.com/v1/badges/3c30167b5fb37a0775ea/maintainability)](https://codeclimate.com/github/AndreiDrang/python3-capsolver/maintainability)
13
+ [![Codacy Badge](https://app.codacy.com/project/badge/Grade/323d4eda0fe1477bbea8fe8902b9e97e)](https://www.codacy.com/gh/AndreiDrang/python3-capsolver/dashboard?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=AndreiDrang/python3-capsolver&amp;utm_campaign=Badge_Grade)
14
+ [![codecov](https://codecov.io/gh/AndreiDrang/python3-capsolver/branch/main/graph/badge.svg?token=2L4VVIF4G8)](https://codecov.io/gh/AndreiDrang/python3-capsolver)
15
+
16
+ [![Sphinx build](https://github.com/AndreiDrang/python3-capsolver/actions/workflows/sphinx.yml/badge.svg?branch=release)](https://github.com/AndreiDrang/python3-capsolver/actions/workflows/sphinx.yml)
17
+ [![Build](https://github.com/AndreiDrang/python3-capsolver/actions/workflows/build.yml/badge.svg?branch=main)](https://github.com/AndreiDrang/python3-capsolver/actions/workflows/build.yml)
18
+ [![Installation](https://github.com/AndreiDrang/python3-capsolver/actions/workflows/install.yml/badge.svg?branch=main)](https://github.com/AndreiDrang/python3-capsolver/actions/workflows/install.yml)
19
+ [![Tests](https://github.com/AndreiDrang/python3-capsolver/actions/workflows/test.yml/badge.svg?branch=main)](https://github.com/AndreiDrang/python3-capsolver/actions/workflows/test.yml)
20
+ [![Lint](https://github.com/AndreiDrang/python3-capsolver/actions/workflows/lint.yml/badge.svg?branch=main)](https://github.com/AndreiDrang/python3-capsolver/actions/workflows/lint.yml)
21
+
22
+
23
+ Python 3 library for [Capsolver](https://dashboard.capsolver.com/passport/register?inviteCode=kQTn-tG07Jb1) service API.
24
+ Tested on UNIX based OS.
25
+
26
+ The library is intended for software developers and is used to work with the [Capsolver](https://dashboard.capsolver.com/passport/register?inviteCode=kQTn-tG07Jb1) service API.
27
+
28
+ ## Features
29
+ - **Modern Tooling**: Uses `uv` for fast, reliable dependency management and environment isolation.
30
+ - **Sync & Async Support**: Full support for both synchronous (`requests`) and asynchronous (`aiohttp`) operations.
31
+ - **Type Safety**: Enums for captcha types and response statuses.
32
+ - **Resilience**: Built-in retries using `tenacity`.
33
+ - **Performance**: High-speed JSON serialization with `msgspec`.
34
+ - **Coverage**: Supports ReCaptcha (V2/V3), Cloudflare, DataDome, GeeTest, MtCaptcha, AWS WAF, Yandex, and ImageToText.
35
+
36
+ ## How to install?
37
+
38
+ We recommend using the latest version of Python. `python3-capsolver` supports Python 3.8+.
39
+
40
+ ### Development (using uv)
41
+
42
+ This project uses [uv](https://github.com/astral-sh/uv) for fast, reliable dependency management.
43
+
44
+ ```bash
45
+ # Install uv (if not already installed)
46
+ curl -LsSf https://astral.sh/uv/install.sh | sh
47
+
48
+ # Install project dependencies
49
+ uv sync --all-groups
50
+ ```
51
+
52
+ ### Production (pip)
53
+
54
+ ```bash
55
+ pip install python3-capsolver
56
+ ```
57
+
58
+ ## How to use?
59
+
60
+ Detailed documentation is available on the [website](https://andreidrang.github.io/python3-capsolver/).
61
+
62
+ ### Quick Start
63
+
64
+ #### Synchronous Example (ImageToText)
65
+ ```python
66
+ from python3_capsolver.image_to_text import ImageToText
67
+
68
+ # 1. Initialize with API Key
69
+ solver = ImageToText(api_key="YOUR_API_KEY")
70
+
71
+ # 2. Solve
72
+ result = solver.captcha_handler(
73
+ task_payload={
74
+ "body": "base64_encoded_image_string"
75
+ }
76
+ )
77
+
78
+ # 3. Check result
79
+ if result["errorId"] == 0:
80
+ print("Solution:", result["solution"])
81
+ else:
82
+ print("Error:", result["errorCode"])
83
+ ```
84
+
85
+ #### Asynchronous Example (ReCaptcha)
86
+ ```python
87
+ import asyncio
88
+ from python3_capsolver.recaptcha import ReCaptcha
89
+ from python3_capsolver.core.enum import CaptchaTypeEnm
90
+
91
+ async def main():
92
+ # 1. Initialize
93
+ solver = ReCaptcha(
94
+ api_key="YOUR_API_KEY",
95
+ captcha_type=CaptchaTypeEnm.ReCaptchaV2TaskProxyLess
96
+ )
97
+
98
+ # 2. Solve
99
+ result = await solver.aio_captcha_handler(
100
+ task_payload={
101
+ "websiteURL": "https://example.com",
102
+ "websiteKey": "SITE_KEY"
103
+ }
104
+ )
105
+
106
+ print(result)
107
+
108
+ if __name__ == "__main__":
109
+ asyncio.run(main())
110
+ ```
111
+
112
+ ## Supported Captcha Types
113
+ - **ReCaptcha**: V2 (Task/Enterprise), V3 (Task/Enterprise)
114
+ - **HCaptcha**: Task, Enterprise
115
+ - **Cloudflare**: Turnstile
116
+ - **GeeTest**: V3, V4
117
+ - **DataDome**: Slider
118
+ - **MtCaptcha**
119
+ - **AWS WAF**
120
+ - **Yandex SmartCaptcha**
121
+ - **ImageToText**: General image CAPTCHAs
122
+
123
+ ## Documentation & Context (For LLMs)
124
+ - **Project Structure**: See `AGENTS.md` in root and subdirectories for internal architecture.
125
+ - **Entry Points**: `src/python3_capsolver/*.py` contains service-specific classes (e.g., `ReCaptcha`, `HCaptcha`).
126
+ - **Core Logic**: `src/python3_capsolver/core/base.py` handles the API communication loop.
127
+ - **Enums**: Use `python3_capsolver.core.enum` for type-safe parameters.
128
+
129
+ ## How to test?
130
+
131
+ The project uses `uv` for testing and development tasks.
132
+
133
+ ```bash
134
+ # 1. Set API_KEY in your environment (get this value from your account)
135
+ export API_KEY="your_api_key_here"
136
+
137
+ # 2. Run tests (uses uv internally)
138
+ make tests
139
+
140
+ # Other useful make targets:
141
+ # make install # Install dependencies with uv sync
142
+ # make lint # Run linting checks
143
+ # make build # Build the package
144
+ # make doc # Generate documentation
145
+ ```
146
+
147
+ All `make` commands automatically use `uv` to run commands in the isolated environment.
148
+
149
+
150
+ ### Changelog
151
+
152
+ See [CHANGELOG.md](https://github.com/AndreiDrang/python3-capsolver/blob/main/CHANGELOG.md) for version history and detailed changes.
153
+
154
+ ### How to get API Key to work with the library
155
+ 1. On the page - https://dashboard.capsolver.com/overview/user-center
156
+ 2. Find it: [![img.png](https://s.vyjava.xyz/files/2024/12-December/17/ae8d4fbf/img.png)](https://vyjava.xyz/dashboard/image/ae8d4fbf-7451-441d-8984-79b1a7adbe27)
157
+
158
+ ### Contacts
159
+
160
+ If you have any questions, please send a message to the [Telegram](https://t.me/pythoncaptcha) chat room.
161
+
162
+ Or email python-captcha@pm.me
@@ -86,7 +86,7 @@ classifiers = [
86
86
  dependencies = [
87
87
  "requests>=2.21.0",
88
88
  "aiohttp>=3.9.2",
89
- "msgspec>=0.18,<=0.21",
89
+ "msgspec>=0.18",
90
90
  "tenacity>=8,<10"
91
91
  ]
92
92
 
@@ -103,3 +103,18 @@ Documentation = "https://andreidrang.github.io/python3-capsolver/"
103
103
  Repository = "https://github.com/AndreiDrang/python3-capsolver"
104
104
  Issues = "https://github.com/AndreiDrang/python3-capsolver/issues"
105
105
  Changelog = "https://github.com/AndreiDrang/python3-capsolver/releases"
106
+
107
+ [dependency-groups]
108
+ dev = [
109
+ "autoflake>=2.3.1",
110
+ "black>=24.8.0",
111
+ "coverage>=7.6.1",
112
+ "isort>=5.13.2",
113
+ "pytest>=8.3.5",
114
+ "pytest-asyncio>=0.24.0",
115
+ ]
116
+ docs = [
117
+ "myst-parser>=3.0.1",
118
+ "pallets-sphinx-themes>=2.3.0",
119
+ "sphinx>=7.1.2",
120
+ ]
@@ -0,0 +1 @@
1
+ __version__ = "1.2.1"
@@ -30,6 +30,8 @@ class AIOCaptchaInstrument(CaptchaInstrumentBase):
30
30
 
31
31
  # if task created and already ready - return result
32
32
  if self.created_task_data.errorId == 0:
33
+ if str(self.created_task_data.status).lower() == ResponseStatusEnm.Ready.value:
34
+ return self.created_task_data.to_dict()
33
35
  return (await self.__get_result()).to_dict()
34
36
  else:
35
37
  self.created_task_data.status = ResponseStatusEnm.Failed
@@ -72,7 +74,10 @@ class AIOCaptchaInstrument(CaptchaInstrumentBase):
72
74
  ) as resp:
73
75
  if resp.status in VALID_STATUS_CODES:
74
76
  result_data = CaptchaResponseSer(**await resp.json())
75
- if result_data.status in (ResponseStatusEnm.Ready, ResponseStatusEnm.Failed):
77
+ if result_data.status in (
78
+ ResponseStatusEnm.Ready,
79
+ ResponseStatusEnm.Failed,
80
+ ):
76
81
  # if captcha ready\failed or have unknown status - return exist data
77
82
  return result_data
78
83
  else:
@@ -90,10 +95,12 @@ class AIOCaptchaInstrument(CaptchaInstrumentBase):
90
95
  self.result.errorDescription = self.CAPTCHA_UNSOLVABLE_DESCRIPTION
91
96
  self.result.taskId = self.created_task_data.taskId
92
97
  self.result.status = ResponseStatusEnm.Failed
98
+ return self.result
93
99
 
94
100
  @staticmethod
95
101
  async def send_post_request(
96
- payload: Optional[dict] = None, url_postfix: EndpointPostfixEnm = EndpointPostfixEnm.GET_BALANCE
102
+ payload: Optional[dict] = None,
103
+ url_postfix: EndpointPostfixEnm = EndpointPostfixEnm.GET_BALANCE,
97
104
  ) -> dict:
98
105
  """
99
106
  Function send ASYNC request to service and wait for result
@@ -83,7 +83,10 @@ class FileInstrument:
83
83
  async for attempt in ASYNC_RETRIES:
84
84
  with attempt:
85
85
  async with session.get(url=url, **kwargs) as resp:
86
- return await resp.content.read()
86
+ if resp.status == 200:
87
+ return await resp.content.read()
88
+ return b""
89
+ return b""
87
90
 
88
91
  def file_processing(
89
92
  self,
@@ -1,12 +1,18 @@
1
1
  from enum import Enum
2
- from typing import Any, Dict, Literal, Optional
2
+ from typing import Any, Dict, Union, Optional
3
3
 
4
4
  from msgspec import Struct
5
5
 
6
6
  from .enum import ResponseStatusEnm
7
7
  from .const import APP_ID
8
8
 
9
- __all__ = ("PostRequestSer", "TaskSer", "RequestCreateTaskSer", "CaptchaResponseSer", "RequestGetTaskResultSer")
9
+ __all__ = (
10
+ "PostRequestSer",
11
+ "TaskSer",
12
+ "RequestCreateTaskSer",
13
+ "CaptchaResponseSer",
14
+ "RequestGetTaskResultSer",
15
+ )
10
16
 
11
17
 
12
18
  class MyBaseModel(Struct):
@@ -36,7 +42,7 @@ class TaskSer(MyBaseModel):
36
42
 
37
43
 
38
44
  class RequestCreateTaskSer(PostRequestSer):
39
- appId: Literal[APP_ID] = APP_ID
45
+ appId: str = APP_ID
40
46
 
41
47
 
42
48
  class RequestGetTaskResultSer(MyBaseModel):
@@ -58,5 +64,5 @@ class ResponseSer(MyBaseModel):
58
64
 
59
65
  class CaptchaResponseSer(ResponseSer):
60
66
  taskId: Optional[str] = None
61
- status: ResponseStatusEnm = ResponseStatusEnm.Processing
67
+ status: Union[ResponseStatusEnm, str] = ResponseStatusEnm.Processing
62
68
  solution: Optional[Dict[str, Any]] = None
@@ -24,6 +24,7 @@ class SIOCaptchaInstrument(CaptchaInstrumentBase):
24
24
  super().__init__()
25
25
  self.captcha_params = captcha_params
26
26
  self.created_task_data = CaptchaResponseSer
27
+
27
28
  # prepare session
28
29
  self.session = requests.Session()
29
30
  self.session.mount("http://", HTTPAdapter(max_retries=RETRIES))
@@ -37,6 +38,8 @@ class SIOCaptchaInstrument(CaptchaInstrumentBase):
37
38
 
38
39
  # if task created and ready - return result
39
40
  if self.created_task_data.errorId == 0:
41
+ if str(self.created_task_data.status).lower() == ResponseStatusEnm.Ready.value:
42
+ return self.created_task_data.to_dict()
40
43
  return self.__get_result().to_dict()
41
44
  else:
42
45
  self.created_task_data.status = ResponseStatusEnm.Failed
@@ -78,7 +81,10 @@ class SIOCaptchaInstrument(CaptchaInstrumentBase):
78
81
  )
79
82
  if resp.status_code in VALID_STATUS_CODES:
80
83
  result_data = CaptchaResponseSer(**resp.json())
81
- if result_data.status in (ResponseStatusEnm.Ready, ResponseStatusEnm.Failed):
84
+ if result_data.status in (
85
+ ResponseStatusEnm.Ready,
86
+ ResponseStatusEnm.Failed,
87
+ ):
82
88
  # if captcha ready\failed or have unknown status - return exist data
83
89
  return result_data
84
90
  else:
@@ -89,12 +95,14 @@ class SIOCaptchaInstrument(CaptchaInstrumentBase):
89
95
 
90
96
  # if captcha just created or in processing now - wait
91
97
  time.sleep(self.captcha_params.sleep_time)
98
+
92
99
  # default response if server is silent
93
100
  self.result.errorId = 1
94
101
  self.result.errorCode = self.CAPTCHA_UNSOLVABLE
95
102
  self.result.errorDescription = self.CAPTCHA_UNSOLVABLE_DESCRIPTION
96
103
  self.result.taskId = self.created_task_data.taskId
97
104
  self.result.status = ResponseStatusEnm.Failed
105
+ return self.result
98
106
 
99
107
  @staticmethod
100
108
  def send_post_request(
@@ -83,7 +83,12 @@ class ImageToText(CaptchaParams):
83
83
  """
84
84
 
85
85
  def __init__(
86
- self, api_key: str, captcha_type: Union[CaptchaTypeEnm, str] = CaptchaTypeEnm.ImageToTextTask, **kwargs
86
+ self,
87
+ api_key: str,
88
+ captcha_type: Union[CaptchaTypeEnm, str] = CaptchaTypeEnm.ImageToTextTask,
89
+ **kwargs,
87
90
  ):
91
+ if isinstance(captcha_type, str):
92
+ captcha_type = CaptchaTypeEnm(captcha_type)
88
93
 
89
94
  super().__init__(api_key=api_key, captcha_type=captcha_type, **kwargs)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python3-capsolver
3
- Version: 1.1.1
3
+ Version: 1.2.1
4
4
  Summary: Python 3.8+ Capsolver library with AIO module.
5
5
  Author-email: AndreiDrang <python-captcha@pm.me>
6
6
  License: MIT License
@@ -30,7 +30,7 @@ Description-Content-Type: text/markdown
30
30
  License-File: LICENSE
31
31
  Requires-Dist: requests>=2.21.0
32
32
  Requires-Dist: aiohttp>=3.9.2
33
- Requires-Dist: msgspec<=0.21,>=0.18
33
+ Requires-Dist: msgspec>=0.18
34
34
  Requires-Dist: tenacity<10,>=8
35
35
  Dynamic: license-file
36
36
 
@@ -61,11 +61,31 @@ Tested on UNIX based OS.
61
61
 
62
62
  The library is intended for software developers and is used to work with the [Capsolver](https://dashboard.capsolver.com/passport/register?inviteCode=kQTn-tG07Jb1) service API.
63
63
 
64
+ ## Features
65
+ - **Modern Tooling**: Uses `uv` for fast, reliable dependency management and environment isolation.
66
+ - **Sync & Async Support**: Full support for both synchronous (`requests`) and asynchronous (`aiohttp`) operations.
67
+ - **Type Safety**: Enums for captcha types and response statuses.
68
+ - **Resilience**: Built-in retries using `tenacity`.
69
+ - **Performance**: High-speed JSON serialization with `msgspec`.
70
+ - **Coverage**: Supports ReCaptcha (V2/V3), Cloudflare, DataDome, GeeTest, MtCaptcha, AWS WAF, Yandex, and ImageToText.
71
+
64
72
  ## How to install?
65
73
 
66
- We recommend using the latest version of Python. `python3-capsolver` supports Python 3.7+.
74
+ We recommend using the latest version of Python. `python3-capsolver` supports Python 3.8+.
75
+
76
+ ### Development (using uv)
67
77
 
68
- ### pip
78
+ This project uses [uv](https://github.com/astral-sh/uv) for fast, reliable dependency management.
79
+
80
+ ```bash
81
+ # Install uv (if not already installed)
82
+ curl -LsSf https://astral.sh/uv/install.sh | sh
83
+
84
+ # Install project dependencies
85
+ uv sync --all-groups
86
+ ```
87
+
88
+ ### Production (pip)
69
89
 
70
90
  ```bash
71
91
  pip install python3-capsolver
@@ -73,18 +93,99 @@ pip install python3-capsolver
73
93
 
74
94
  ## How to use?
75
95
 
76
- Is described in the [documentation-website](https://andreidrang.github.io/python3-capsolver/).
96
+ Detailed documentation is available on the [website](https://andreidrang.github.io/python3-capsolver/).
97
+
98
+ ### Quick Start
99
+
100
+ #### Synchronous Example (ImageToText)
101
+ ```python
102
+ from python3_capsolver.image_to_text import ImageToText
77
103
 
104
+ # 1. Initialize with API Key
105
+ solver = ImageToText(api_key="YOUR_API_KEY")
106
+
107
+ # 2. Solve
108
+ result = solver.captcha_handler(
109
+ task_payload={
110
+ "body": "base64_encoded_image_string"
111
+ }
112
+ )
113
+
114
+ # 3. Check result
115
+ if result["errorId"] == 0:
116
+ print("Solution:", result["solution"])
117
+ else:
118
+ print("Error:", result["errorCode"])
119
+ ```
120
+
121
+ #### Asynchronous Example (ReCaptcha)
122
+ ```python
123
+ import asyncio
124
+ from python3_capsolver.recaptcha import ReCaptcha
125
+ from python3_capsolver.core.enum import CaptchaTypeEnm
126
+
127
+ async def main():
128
+ # 1. Initialize
129
+ solver = ReCaptcha(
130
+ api_key="YOUR_API_KEY",
131
+ captcha_type=CaptchaTypeEnm.ReCaptchaV2TaskProxyLess
132
+ )
133
+
134
+ # 2. Solve
135
+ result = await solver.aio_captcha_handler(
136
+ task_payload={
137
+ "websiteURL": "https://example.com",
138
+ "websiteKey": "SITE_KEY"
139
+ }
140
+ )
141
+
142
+ print(result)
143
+
144
+ if __name__ == "__main__":
145
+ asyncio.run(main())
146
+ ```
147
+
148
+ ## Supported Captcha Types
149
+ - **ReCaptcha**: V2 (Task/Enterprise), V3 (Task/Enterprise)
150
+ - **HCaptcha**: Task, Enterprise
151
+ - **Cloudflare**: Turnstile
152
+ - **GeeTest**: V3, V4
153
+ - **DataDome**: Slider
154
+ - **MtCaptcha**
155
+ - **AWS WAF**
156
+ - **Yandex SmartCaptcha**
157
+ - **ImageToText**: General image CAPTCHAs
158
+
159
+ ## Documentation & Context (For LLMs)
160
+ - **Project Structure**: See `AGENTS.md` in root and subdirectories for internal architecture.
161
+ - **Entry Points**: `src/python3_capsolver/*.py` contains service-specific classes (e.g., `ReCaptcha`, `HCaptcha`).
162
+ - **Core Logic**: `src/python3_capsolver/core/base.py` handles the API communication loop.
163
+ - **Enums**: Use `python3_capsolver.core.enum` for type-safe parameters.
78
164
 
79
165
  ## How to test?
80
166
 
81
- 1. You need set ``API_KEY`` in your environment(get this value from you account).
82
- 2. Run command ``make tests``, from root directory.
167
+ The project uses `uv` for testing and development tasks.
168
+
169
+ ```bash
170
+ # 1. Set API_KEY in your environment (get this value from your account)
171
+ export API_KEY="your_api_key_here"
172
+
173
+ # 2. Run tests (uses uv internally)
174
+ make tests
175
+
176
+ # Other useful make targets:
177
+ # make install # Install dependencies with uv sync
178
+ # make lint # Run linting checks
179
+ # make build # Build the package
180
+ # make doc # Generate documentation
181
+ ```
182
+
183
+ All `make` commands automatically use `uv` to run commands in the isolated environment.
83
184
 
84
185
 
85
186
  ### Changelog
86
187
 
87
- Check [releases page](https://github.com/AndreiDrang/python3-capsolver/releases).
188
+ See [CHANGELOG.md](https://github.com/AndreiDrang/python3-capsolver/blob/main/CHANGELOG.md) for version history and detailed changes.
88
189
 
89
190
  ### How to get API Key to work with the library
90
191
  1. On the page - https://dashboard.capsolver.com/overview/user-center
@@ -1,4 +1,4 @@
1
1
  requests>=2.21.0
2
2
  aiohttp>=3.9.2
3
- msgspec<=0.21,>=0.18
3
+ msgspec>=0.18
4
4
  tenacity<10,>=8
@@ -0,0 +1,175 @@
1
+ import pytest
2
+ from unittest.mock import patch, MagicMock, AsyncMock
3
+
4
+ from tests.conftest import BaseTest
5
+ from python3_capsolver.control import Control
6
+
7
+
8
+ class TestControl(BaseTest):
9
+ """
10
+ Success tests
11
+ """
12
+
13
+ def test_get_balance_exist(self):
14
+ assert "get_balance" in Control.__dict__.keys()
15
+ assert "aio_get_balance" in Control.__dict__.keys()
16
+
17
+ def test_get_balance(self):
18
+ result = Control(api_key=self.API_KEY).get_balance()
19
+ assert isinstance(result, dict)
20
+ assert result["errorId"] == 0
21
+ assert result["balance"] != 0.0
22
+
23
+ async def test_aio_get_balance(self):
24
+ result = await Control(api_key=self.API_KEY).aio_get_balance()
25
+ assert isinstance(result, dict)
26
+ assert result["errorId"] == 0
27
+ assert result["balance"] != 0.0
28
+
29
+ """
30
+ Failed tests
31
+ """
32
+
33
+ def test_get_balance_api_key_err(self):
34
+ with pytest.raises(ValueError):
35
+ Control(api_key=self.get_random_string(36)).get_balance()
36
+
37
+ async def test_aio_get_balance_api_key_err(self):
38
+ with pytest.raises(ValueError):
39
+ await Control(api_key=self.get_random_string(36)).aio_get_balance()
40
+
41
+
42
+ class TestControlMock(BaseTest):
43
+ """
44
+ Mocked Unit tests for Control class
45
+ """
46
+
47
+ @patch("python3_capsolver.core.sio_captcha_instrument.requests.Session.post")
48
+ def test_create_task(self, mock_post):
49
+ mock_response = MagicMock()
50
+ mock_response.status_code = 200
51
+ mock_response.json.return_value = {"errorId": 0, "taskId": "test-task-id"}
52
+ mock_post.return_value = mock_response
53
+
54
+ control = Control(api_key="test-key")
55
+ result = control.create_task({"type": "ImageToTextTask", "body": "base64..."})
56
+
57
+ assert result["taskId"] == "test-task-id"
58
+ assert result["errorId"] == 0
59
+ mock_post.assert_called_once()
60
+
61
+ @patch("python3_capsolver.core.aio_captcha_instrument.aiohttp.ClientSession.post")
62
+ async def test_aio_create_task(self, mock_post):
63
+ mock_resp = MagicMock()
64
+ mock_resp.status = 200
65
+ mock_resp.json = AsyncMock(
66
+ return_value={"errorId": 0, "taskId": "test-task-id"}
67
+ )
68
+ # Mock async context manager
69
+ mock_resp.__aenter__.return_value = mock_resp
70
+ mock_post.return_value = mock_resp
71
+
72
+ control = Control(api_key="test-key")
73
+ result = await control.aio_create_task(
74
+ {"type": "ImageToTextTask", "body": "base64..."}
75
+ )
76
+
77
+ assert result["taskId"] == "test-task-id"
78
+ assert result["errorId"] == 0
79
+
80
+ @patch("python3_capsolver.core.sio_captcha_instrument.requests.Session.post")
81
+ def test_get_task_result(self, mock_post):
82
+ mock_response = MagicMock()
83
+ mock_response.status_code = 200
84
+ mock_response.json.return_value = {
85
+ "errorId": 0,
86
+ "status": "ready",
87
+ "solution": {"text": "abc"},
88
+ }
89
+ mock_post.return_value = mock_response
90
+
91
+ control = Control(api_key="test-key")
92
+ result = control.get_task_result(task_id="test-id")
93
+
94
+ assert result["status"] == "ready"
95
+ assert result["solution"]["text"] == "abc"
96
+
97
+ @patch("python3_capsolver.core.aio_captcha_instrument.aiohttp.ClientSession.post")
98
+ async def test_aio_get_task_result(self, mock_post):
99
+ mock_resp = MagicMock()
100
+ mock_resp.status = 200
101
+ mock_resp.json = AsyncMock(
102
+ return_value={
103
+ "errorId": 0,
104
+ "status": "ready",
105
+ "solution": {"text": "abc"},
106
+ }
107
+ )
108
+ mock_resp.__aenter__.return_value = mock_resp
109
+ mock_post.return_value = mock_resp
110
+
111
+ control = Control(api_key="test-key")
112
+ result = await control.aio_get_task_result(task_id="test-id")
113
+
114
+ assert result["status"] == "ready"
115
+ assert result["solution"]["text"] == "abc"
116
+
117
+ @patch("python3_capsolver.core.sio_captcha_instrument.requests.Session.post")
118
+ def test_get_token(self, mock_post):
119
+ mock_response = MagicMock()
120
+ mock_response.status_code = 200
121
+ mock_response.json.return_value = {"errorId": 0, "taskId": "token-task-id"}
122
+ mock_post.return_value = mock_response
123
+
124
+ control = Control(api_key="test-key")
125
+ result = control.get_token(
126
+ {"type": "ReCaptchaV3TaskProxyLess", "websiteURL": "..."}
127
+ )
128
+
129
+ assert result["taskId"] == "token-task-id"
130
+
131
+ @patch("python3_capsolver.core.aio_captcha_instrument.aiohttp.ClientSession.post")
132
+ async def test_aio_get_token(self, mock_post):
133
+ mock_resp = MagicMock()
134
+ mock_resp.status = 200
135
+ mock_resp.json = AsyncMock(
136
+ return_value={"errorId": 0, "taskId": "token-task-id"}
137
+ )
138
+ mock_resp.__aenter__.return_value = mock_resp
139
+ mock_post.return_value = mock_resp
140
+
141
+ control = Control(api_key="test-key")
142
+ result = await control.aio_get_token(
143
+ {"type": "ReCaptchaV3TaskProxyLess", "websiteURL": "..."}
144
+ )
145
+
146
+ assert result["taskId"] == "token-task-id"
147
+
148
+ @patch("python3_capsolver.core.sio_captcha_instrument.requests.Session.post")
149
+ def test_feedback_task(self, mock_post):
150
+ mock_response = MagicMock()
151
+ mock_response.status_code = 200
152
+ mock_response.json.return_value = {"errorId": 0, "message": "okay"}
153
+ mock_post.return_value = mock_response
154
+
155
+ control = Control(api_key="test-key")
156
+ result = control.feedback_task(
157
+ task_id="test-id", result_payload={"invalid": True}
158
+ )
159
+
160
+ assert result["message"] == "okay"
161
+
162
+ @patch("python3_capsolver.core.aio_captcha_instrument.aiohttp.ClientSession.post")
163
+ async def test_aio_feedback_task(self, mock_post):
164
+ mock_resp = MagicMock()
165
+ mock_resp.status = 200
166
+ mock_resp.json = AsyncMock(return_value={"errorId": 0, "message": "okay"})
167
+ mock_resp.__aenter__.return_value = mock_resp
168
+ mock_post.return_value = mock_resp
169
+
170
+ control = Control(api_key="test-key")
171
+ result = await control.aio_feedback_task(
172
+ task_id="test-id", result_payload={"invalid": True}
173
+ )
174
+
175
+ assert result["message"] == "okay"
@@ -1,61 +0,0 @@
1
- # python3-capsolver
2
-
3
- [![Capsolver.png](https://s.vyjava.xyz/files/2024/12-December/17/109278aa/Capsolver.png)](https://vyjava.xyz/dashboard/image/109278aa-961a-4503-bed0-0a9c838dcef2)
4
-
5
- <hr>
6
-
7
- [![PyPI version](https://badge.fury.io/py/python3-capsolver.svg)](https://badge.fury.io/py/python3-capsolver)
8
- [![Python versions](https://img.shields.io/pypi/pyversions/python3-capsolver.svg?logo=python&logoColor=FBE072)](https://badge.fury.io/py/python3-capsolver)
9
- [![Downloads](https://static.pepy.tech/badge/python3-capsolver/month)](https://pepy.tech/project/python3-capsolver)
10
- [![Static Badge](https://img.shields.io/badge/docs-Sphinx-green?label=Documentation&labelColor=gray)](https://andreidrang.github.io/python3-capsolver/)
11
-
12
- [![Maintainability](https://api.codeclimate.com/v1/badges/3c30167b5fb37a0775ea/maintainability)](https://codeclimate.com/github/AndreiDrang/python3-capsolver/maintainability)
13
- [![Codacy Badge](https://app.codacy.com/project/badge/Grade/323d4eda0fe1477bbea8fe8902b9e97e)](https://www.codacy.com/gh/AndreiDrang/python3-capsolver/dashboard?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=AndreiDrang/python3-capsolver&amp;utm_campaign=Badge_Grade)
14
- [![codecov](https://codecov.io/gh/AndreiDrang/python3-capsolver/branch/main/graph/badge.svg?token=2L4VVIF4G8)](https://codecov.io/gh/AndreiDrang/python3-capsolver)
15
-
16
- [![Sphinx build](https://github.com/AndreiDrang/python3-capsolver/actions/workflows/sphinx.yml/badge.svg?branch=release)](https://github.com/AndreiDrang/python3-capsolver/actions/workflows/sphinx.yml)
17
- [![Build](https://github.com/AndreiDrang/python3-capsolver/actions/workflows/build.yml/badge.svg?branch=main)](https://github.com/AndreiDrang/python3-capsolver/actions/workflows/build.yml)
18
- [![Installation](https://github.com/AndreiDrang/python3-capsolver/actions/workflows/install.yml/badge.svg?branch=main)](https://github.com/AndreiDrang/python3-capsolver/actions/workflows/install.yml)
19
- [![Tests](https://github.com/AndreiDrang/python3-capsolver/actions/workflows/test.yml/badge.svg?branch=main)](https://github.com/AndreiDrang/python3-capsolver/actions/workflows/test.yml)
20
- [![Lint](https://github.com/AndreiDrang/python3-capsolver/actions/workflows/lint.yml/badge.svg?branch=main)](https://github.com/AndreiDrang/python3-capsolver/actions/workflows/lint.yml)
21
-
22
-
23
- Python 3 library for [Capsolver](https://dashboard.capsolver.com/passport/register?inviteCode=kQTn-tG07Jb1) service API.
24
- Tested on UNIX based OS.
25
-
26
- The library is intended for software developers and is used to work with the [Capsolver](https://dashboard.capsolver.com/passport/register?inviteCode=kQTn-tG07Jb1) service API.
27
-
28
- ## How to install?
29
-
30
- We recommend using the latest version of Python. `python3-capsolver` supports Python 3.7+.
31
-
32
- ### pip
33
-
34
- ```bash
35
- pip install python3-capsolver
36
- ```
37
-
38
- ## How to use?
39
-
40
- Is described in the [documentation-website](https://andreidrang.github.io/python3-capsolver/).
41
-
42
-
43
- ## How to test?
44
-
45
- 1. You need set ``API_KEY`` in your environment(get this value from you account).
46
- 2. Run command ``make tests``, from root directory.
47
-
48
-
49
- ### Changelog
50
-
51
- Check [releases page](https://github.com/AndreiDrang/python3-capsolver/releases).
52
-
53
- ### How to get API Key to work with the library
54
- 1. On the page - https://dashboard.capsolver.com/overview/user-center
55
- 2. Find it: [![img.png](https://s.vyjava.xyz/files/2024/12-December/17/ae8d4fbf/img.png)](https://vyjava.xyz/dashboard/image/ae8d4fbf-7451-441d-8984-79b1a7adbe27)
56
-
57
- ### Contacts
58
-
59
- If you have any questions, please send a message to the [Telegram](https://t.me/pythoncaptcha) chat room.
60
-
61
- Or email python-captcha@pm.me
@@ -1 +0,0 @@
1
- __version__ = "1.1.1"
@@ -1,38 +0,0 @@
1
- import pytest
2
-
3
- from tests.conftest import BaseTest
4
- from python3_capsolver.control import Control
5
-
6
-
7
- class TestControl(BaseTest):
8
- """
9
- Success tests
10
- """
11
-
12
- def test_get_balance_exist(self):
13
- assert "get_balance" in Control.__dict__.keys()
14
- assert "aio_get_balance" in Control.__dict__.keys()
15
-
16
- def test_get_balance(self):
17
- result = Control(api_key=self.API_KEY).get_balance()
18
- assert isinstance(result, dict)
19
- assert result["errorId"] == 0
20
- assert result["balance"] != 0.0
21
-
22
- async def test_aio_get_balance(self):
23
- result = await Control(api_key=self.API_KEY).aio_get_balance()
24
- assert isinstance(result, dict)
25
- assert result["errorId"] == 0
26
- assert result["balance"] != 0.0
27
-
28
- """
29
- Failed tests
30
- """
31
-
32
- def test_get_balance_api_key_err(self):
33
- with pytest.raises(ValueError):
34
- Control(api_key=self.get_random_string(36)).get_balance()
35
-
36
- async def test_aio_get_balance_api_key_err(self):
37
- with pytest.raises(ValueError):
38
- await Control(api_key=self.get_random_string(36)).aio_get_balance()