fileboy 0.1.0__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.
fileboy-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) CD2 Solutions Ltd
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
fileboy-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,207 @@
1
+ Metadata-Version: 2.4
2
+ Name: fileboy
3
+ Version: 0.1.0
4
+ Summary: Official Python client for Fileboy: upload files and videos, build image transform URLs, read video data.
5
+ Author: Fileboy
6
+ License: MIT
7
+ Project-URL: Homepage, https://fileboy.io/docs/libraries/python
8
+ Project-URL: Documentation, https://fileboy.io/docs/libraries/python
9
+ Project-URL: Changelog, https://github.com/CD2/Fileboy/blob/master/packages/python/CHANGELOG.md
10
+ Keywords: fileboy,file upload,image resize,video hosting,sdk
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3 :: Only
17
+ Classifier: Programming Language :: Python :: 3.8
18
+ Classifier: Programming Language :: Python :: 3.9
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Topic :: Internet :: WWW/HTTP
24
+ Classifier: Typing :: Typed
25
+ Requires-Python: >=3.8
26
+ Description-Content-Type: text/markdown
27
+ License-File: LICENSE
28
+ Requires-Dist: requests>=2.25
29
+ Provides-Extra: dev
30
+ Requires-Dist: pytest>=7; extra == "dev"
31
+ Requires-Dist: requests>=2.25; extra == "dev"
32
+ Dynamic: license-file
33
+
34
+ # Fileboy for Python
35
+
36
+ The official Python client for [Fileboy](https://fileboy.io): upload files and videos, build image
37
+ URLs that resize on the fly, and read video data. Its only dependency is `requests`.
38
+
39
+ ## Install
40
+
41
+ ```bash
42
+ pip install fileboy
43
+ ```
44
+
45
+ Python 3.8 or newer.
46
+
47
+ ## Quick start
48
+
49
+ ```python
50
+ import os
51
+ import fileboy
52
+
53
+ client = fileboy.Client(public_key=os.environ["FILEBOY_PUBLIC_KEY"])
54
+
55
+ file = client.upload_file("report.pdf") # a path, a binary file object or bytes
56
+ print(file.id) # store this
57
+
58
+ client.build_file_url(file.id, download=True)
59
+ client.build_image_url(file.id, transforms=[{"type": "resize", "width": 300}, {"type": "format", "format": "webp"}])
60
+ # https://api.fileboy.io/images/get/<id>?transform=resize:300x_;format:webp
61
+ ```
62
+
63
+ ## Django and other frameworks
64
+
65
+ Configure once, then call `fileboy.client()` anywhere:
66
+
67
+ ```python
68
+ # settings.py (or an AppConfig.ready())
69
+ import os
70
+ import fileboy
71
+
72
+ fileboy.configure(
73
+ public_key=os.environ["FILEBOY_PUBLIC_KEY"],
74
+ # private_key=..., only if your app uses signed requests
75
+ # api_url="https://api.fileboy.io",
76
+ )
77
+ ```
78
+
79
+ ```python
80
+ # views.py
81
+ import fileboy
82
+
83
+ def upload(request):
84
+ file = fileboy.client().upload_file(request.FILES["file"], name=request.FILES["file"].name)
85
+ ...
86
+ ```
87
+
88
+ `FILEBOY_PUBLIC_KEY`, `FILEBOY_PRIVATE_KEY` and `FILEBOY_API_URL` are read from the environment when you do not set them.
89
+
90
+ ## What you can do
91
+
92
+ | Task | Method |
93
+ | --- | --- |
94
+ | Upload a path, file object or bytes | `upload_file(file, name=, mime=, metadata=)` |
95
+ | Upload from a path | `upload_file_from_path(path, ...)` |
96
+ | Upload from a URL | `upload_file_from_url(url, name=, metadata=, expect=)` |
97
+ | File details | `fetch_file_data(id_or_label)` |
98
+ | File URL | `build_file_url(id_or_label, filename=, download=)` |
99
+ | Image URL with transforms | `build_image_url(id_or_label, transforms=, filename=, download=)` |
100
+ | Download bytes | `download_file(id_or_label, range=)`, `download_image(id_or_label, transforms=, range=)` |
101
+ | Upload a video | `upload_video(file, name=, mime=)` |
102
+ | Video details | `fetch_video_data(id)`, `fetch_video_info(id)` |
103
+ | Manifest and poster URLs | `build_video_manifest_url(id, type="hls")`, `build_video_poster_url(id)` |
104
+ | Custom poster | `upload_video_poster(video_id, image)`, `download_video_poster(video_id)` |
105
+ | Transcripts | `upload_video_transcript(id, file, language="English")`, `fetch_video_transcript(id, transcript_id)` |
106
+ | Signed access token | `create_access_token(read=, write=, upload=, expires_in=)` |
107
+
108
+ An id that is a UUID is used as is. Anything else is treated as a label in your app, and a value
109
+ that already contains a `+` is passed through. Labels are created in the dashboard.
110
+
111
+ Uploads return a `FileData` dataclass (`id`, `name`, `original_filename`, `mime`, `metadata`,
112
+ `file_size`, `content_hash`, `signed`, `dies_at`, `created_at`). `upload_video` returns a `Video`
113
+ (`id`, `name`, `raw_file_id`). `range` is a `(first_byte, last_byte)` tuple.
114
+
115
+ ### Image transforms
116
+
117
+ `transforms=` takes a list of dicts applied in order. The image service supports `resize`,
118
+ `format`, `quality`, `background` and `invert`:
119
+
120
+ ```python
121
+ [
122
+ {"type": "resize", "width": 200, "height": 200, "fit": "cover"}, # fit: contain cover fill inside outside
123
+ {"type": "resize", "width": 400, "fit": "contain", "position": "top-left", "background": "#ffffff"},
124
+ {"type": "format", "format": "webp"}, # jpeg png webp tiff bmp
125
+ {"type": "quality", "value": 75},
126
+ {"type": "background", "color": "#fff"},
127
+ {"type": "invert"},
128
+ ]
129
+ ```
130
+
131
+ You can also pass a dict of `name: value`, such as `{"resize": "300x_", "format": "webp", "quality": 75}`, or a ready-made string.
132
+
133
+ ## Errors
134
+
135
+ Every error is a `FileboyError` with `status`, `code` (the API's error code) and `body`.
136
+
137
+ | Class | When |
138
+ | --- | --- |
139
+ | `AuthenticationError` | 401 |
140
+ | `FileboyPermissionError` | 403, including plan limits |
141
+ | `NotFoundError` | 404 |
142
+ | `RateLimitError` | 429 |
143
+ | `BadRequestError` | other 4xx, such as a file that is too large |
144
+ | `ServerError` | 5xx |
145
+ | `FileboyConnectionError` | timeouts and network failures |
146
+ | `InvalidArgumentError`, `ConfigurationError` | bad arguments, before any request |
147
+
148
+ `FileboyPermissionError` and `FileboyConnectionError` are prefixed so they do not shadow the
149
+ Python built-ins `PermissionError` and `ConnectionError`.
150
+
151
+ ## Notes
152
+
153
+ * File, image and video reads need no authorization, so URL builders never sign anything.
154
+ * The shared `demo` key only accepts small images and cannot upload video.
155
+ * There is no endpoint yet to update, delete or list files, so the client has no such methods.
156
+ * The client uses a `requests.Session`, so connections are reused between calls.
157
+
158
+ ## Development
159
+
160
+ ```bash
161
+ pip install -e ".[dev]"
162
+ pytest
163
+ ```
164
+
165
+ The tests run against a local fake server. The transform builder is also checked against
166
+ `packages/sdk-fixtures/transforms.json`, the same cases the Go image parser is tested with.
167
+
168
+ MIT licensed.
169
+
170
+ ## Webhooks
171
+
172
+ Verify the requests Fileboy sends to your endpoints. Use the raw body, exactly as received:
173
+
174
+ ```python
175
+ # Django
176
+ import fileboy
177
+ from django.http import HttpResponse, HttpResponseBadRequest
178
+ from django.views.decorators.csrf import csrf_exempt
179
+
180
+ @csrf_exempt
181
+ def fileboy_webhook(request):
182
+ try:
183
+ event = fileboy.verify_webhook(request.body, request.headers, settings.FILEBOY_WEBHOOK_SECRET)
184
+ except fileboy.WebhookVerificationError as error:
185
+ return HttpResponseBadRequest(error.code) # "invalid_signature", "timestamp_out_of_tolerance", ...
186
+ if event["type"] == "video.ready":
187
+ ... # event["resource"]["urls"]["hls"], event["resource"]["duration"]
188
+ return HttpResponse(status=200)
189
+ ```
190
+
191
+ `verify_webhook` returns the parsed event (a dict) or raises `WebhookVerificationError`. The timestamp
192
+ must be within 5 minutes (`tolerance_seconds=` changes that). Events carry a unique `id` that is the same
193
+ on every retry, so use it to ignore duplicates. `fileboy.sign_webhook(body, timestamp, secret)` builds a
194
+ signature for your own tests.
195
+
196
+ ## Publishing (maintainers)
197
+
198
+ You need a [PyPI](https://pypi.org) account that owns `fileboy` (the first upload claims the name) and an API token (account settings, scope: that project once it exists). Try [TestPyPI](https://test.pypi.org) first.
199
+
200
+ 1. Bump the version in `src/fileboy/_version.py` and `pyproject.toml` (keep them equal) and add a section to `CHANGELOG.md`.
201
+ 2. `pip install -e '.[dev]' && pytest` must pass.
202
+ 3. `python -m pip install build twine && rm -rf dist && python -m build && twine check dist/*`
203
+ 4. Rehearse: `twine upload --repository testpypi dist/*`.
204
+ 5. Publish: `twine upload dist/*` with username `__token__` and the API token as the password.
205
+ 6. Tag the commit: `git tag python-vX.Y.Z && git push origin python-vX.Y.Z`.
206
+
207
+ PyPI never lets you reuse a version number, so bump it for every fix.
@@ -0,0 +1,174 @@
1
+ # Fileboy for Python
2
+
3
+ The official Python client for [Fileboy](https://fileboy.io): upload files and videos, build image
4
+ URLs that resize on the fly, and read video data. Its only dependency is `requests`.
5
+
6
+ ## Install
7
+
8
+ ```bash
9
+ pip install fileboy
10
+ ```
11
+
12
+ Python 3.8 or newer.
13
+
14
+ ## Quick start
15
+
16
+ ```python
17
+ import os
18
+ import fileboy
19
+
20
+ client = fileboy.Client(public_key=os.environ["FILEBOY_PUBLIC_KEY"])
21
+
22
+ file = client.upload_file("report.pdf") # a path, a binary file object or bytes
23
+ print(file.id) # store this
24
+
25
+ client.build_file_url(file.id, download=True)
26
+ client.build_image_url(file.id, transforms=[{"type": "resize", "width": 300}, {"type": "format", "format": "webp"}])
27
+ # https://api.fileboy.io/images/get/<id>?transform=resize:300x_;format:webp
28
+ ```
29
+
30
+ ## Django and other frameworks
31
+
32
+ Configure once, then call `fileboy.client()` anywhere:
33
+
34
+ ```python
35
+ # settings.py (or an AppConfig.ready())
36
+ import os
37
+ import fileboy
38
+
39
+ fileboy.configure(
40
+ public_key=os.environ["FILEBOY_PUBLIC_KEY"],
41
+ # private_key=..., only if your app uses signed requests
42
+ # api_url="https://api.fileboy.io",
43
+ )
44
+ ```
45
+
46
+ ```python
47
+ # views.py
48
+ import fileboy
49
+
50
+ def upload(request):
51
+ file = fileboy.client().upload_file(request.FILES["file"], name=request.FILES["file"].name)
52
+ ...
53
+ ```
54
+
55
+ `FILEBOY_PUBLIC_KEY`, `FILEBOY_PRIVATE_KEY` and `FILEBOY_API_URL` are read from the environment when you do not set them.
56
+
57
+ ## What you can do
58
+
59
+ | Task | Method |
60
+ | --- | --- |
61
+ | Upload a path, file object or bytes | `upload_file(file, name=, mime=, metadata=)` |
62
+ | Upload from a path | `upload_file_from_path(path, ...)` |
63
+ | Upload from a URL | `upload_file_from_url(url, name=, metadata=, expect=)` |
64
+ | File details | `fetch_file_data(id_or_label)` |
65
+ | File URL | `build_file_url(id_or_label, filename=, download=)` |
66
+ | Image URL with transforms | `build_image_url(id_or_label, transforms=, filename=, download=)` |
67
+ | Download bytes | `download_file(id_or_label, range=)`, `download_image(id_or_label, transforms=, range=)` |
68
+ | Upload a video | `upload_video(file, name=, mime=)` |
69
+ | Video details | `fetch_video_data(id)`, `fetch_video_info(id)` |
70
+ | Manifest and poster URLs | `build_video_manifest_url(id, type="hls")`, `build_video_poster_url(id)` |
71
+ | Custom poster | `upload_video_poster(video_id, image)`, `download_video_poster(video_id)` |
72
+ | Transcripts | `upload_video_transcript(id, file, language="English")`, `fetch_video_transcript(id, transcript_id)` |
73
+ | Signed access token | `create_access_token(read=, write=, upload=, expires_in=)` |
74
+
75
+ An id that is a UUID is used as is. Anything else is treated as a label in your app, and a value
76
+ that already contains a `+` is passed through. Labels are created in the dashboard.
77
+
78
+ Uploads return a `FileData` dataclass (`id`, `name`, `original_filename`, `mime`, `metadata`,
79
+ `file_size`, `content_hash`, `signed`, `dies_at`, `created_at`). `upload_video` returns a `Video`
80
+ (`id`, `name`, `raw_file_id`). `range` is a `(first_byte, last_byte)` tuple.
81
+
82
+ ### Image transforms
83
+
84
+ `transforms=` takes a list of dicts applied in order. The image service supports `resize`,
85
+ `format`, `quality`, `background` and `invert`:
86
+
87
+ ```python
88
+ [
89
+ {"type": "resize", "width": 200, "height": 200, "fit": "cover"}, # fit: contain cover fill inside outside
90
+ {"type": "resize", "width": 400, "fit": "contain", "position": "top-left", "background": "#ffffff"},
91
+ {"type": "format", "format": "webp"}, # jpeg png webp tiff bmp
92
+ {"type": "quality", "value": 75},
93
+ {"type": "background", "color": "#fff"},
94
+ {"type": "invert"},
95
+ ]
96
+ ```
97
+
98
+ You can also pass a dict of `name: value`, such as `{"resize": "300x_", "format": "webp", "quality": 75}`, or a ready-made string.
99
+
100
+ ## Errors
101
+
102
+ Every error is a `FileboyError` with `status`, `code` (the API's error code) and `body`.
103
+
104
+ | Class | When |
105
+ | --- | --- |
106
+ | `AuthenticationError` | 401 |
107
+ | `FileboyPermissionError` | 403, including plan limits |
108
+ | `NotFoundError` | 404 |
109
+ | `RateLimitError` | 429 |
110
+ | `BadRequestError` | other 4xx, such as a file that is too large |
111
+ | `ServerError` | 5xx |
112
+ | `FileboyConnectionError` | timeouts and network failures |
113
+ | `InvalidArgumentError`, `ConfigurationError` | bad arguments, before any request |
114
+
115
+ `FileboyPermissionError` and `FileboyConnectionError` are prefixed so they do not shadow the
116
+ Python built-ins `PermissionError` and `ConnectionError`.
117
+
118
+ ## Notes
119
+
120
+ * File, image and video reads need no authorization, so URL builders never sign anything.
121
+ * The shared `demo` key only accepts small images and cannot upload video.
122
+ * There is no endpoint yet to update, delete or list files, so the client has no such methods.
123
+ * The client uses a `requests.Session`, so connections are reused between calls.
124
+
125
+ ## Development
126
+
127
+ ```bash
128
+ pip install -e ".[dev]"
129
+ pytest
130
+ ```
131
+
132
+ The tests run against a local fake server. The transform builder is also checked against
133
+ `packages/sdk-fixtures/transforms.json`, the same cases the Go image parser is tested with.
134
+
135
+ MIT licensed.
136
+
137
+ ## Webhooks
138
+
139
+ Verify the requests Fileboy sends to your endpoints. Use the raw body, exactly as received:
140
+
141
+ ```python
142
+ # Django
143
+ import fileboy
144
+ from django.http import HttpResponse, HttpResponseBadRequest
145
+ from django.views.decorators.csrf import csrf_exempt
146
+
147
+ @csrf_exempt
148
+ def fileboy_webhook(request):
149
+ try:
150
+ event = fileboy.verify_webhook(request.body, request.headers, settings.FILEBOY_WEBHOOK_SECRET)
151
+ except fileboy.WebhookVerificationError as error:
152
+ return HttpResponseBadRequest(error.code) # "invalid_signature", "timestamp_out_of_tolerance", ...
153
+ if event["type"] == "video.ready":
154
+ ... # event["resource"]["urls"]["hls"], event["resource"]["duration"]
155
+ return HttpResponse(status=200)
156
+ ```
157
+
158
+ `verify_webhook` returns the parsed event (a dict) or raises `WebhookVerificationError`. The timestamp
159
+ must be within 5 minutes (`tolerance_seconds=` changes that). Events carry a unique `id` that is the same
160
+ on every retry, so use it to ignore duplicates. `fileboy.sign_webhook(body, timestamp, secret)` builds a
161
+ signature for your own tests.
162
+
163
+ ## Publishing (maintainers)
164
+
165
+ You need a [PyPI](https://pypi.org) account that owns `fileboy` (the first upload claims the name) and an API token (account settings, scope: that project once it exists). Try [TestPyPI](https://test.pypi.org) first.
166
+
167
+ 1. Bump the version in `src/fileboy/_version.py` and `pyproject.toml` (keep them equal) and add a section to `CHANGELOG.md`.
168
+ 2. `pip install -e '.[dev]' && pytest` must pass.
169
+ 3. `python -m pip install build twine && rm -rf dist && python -m build && twine check dist/*`
170
+ 4. Rehearse: `twine upload --repository testpypi dist/*`.
171
+ 5. Publish: `twine upload dist/*` with username `__token__` and the API token as the password.
172
+ 6. Tag the commit: `git tag python-vX.Y.Z && git push origin python-vX.Y.Z`.
173
+
174
+ PyPI never lets you reuse a version number, so bump it for every fix.
@@ -0,0 +1,47 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "fileboy"
7
+ version = "0.1.0"
8
+ description = "Official Python client for Fileboy: upload files and videos, build image transform URLs, read video data."
9
+ readme = "README.md"
10
+ license = { text = "MIT" }
11
+ requires-python = ">=3.8"
12
+ authors = [{ name = "Fileboy" }]
13
+ keywords = ["fileboy", "file upload", "image resize", "video hosting", "sdk"]
14
+ classifiers = [
15
+ "Development Status :: 4 - Beta",
16
+ "Intended Audience :: Developers",
17
+ "License :: OSI Approved :: MIT License",
18
+ "Operating System :: OS Independent",
19
+ "Programming Language :: Python :: 3",
20
+ "Programming Language :: Python :: 3 :: Only",
21
+ "Programming Language :: Python :: 3.8",
22
+ "Programming Language :: Python :: 3.9",
23
+ "Programming Language :: Python :: 3.10",
24
+ "Programming Language :: Python :: 3.11",
25
+ "Programming Language :: Python :: 3.12",
26
+ "Programming Language :: Python :: 3.13",
27
+ "Topic :: Internet :: WWW/HTTP",
28
+ "Typing :: Typed",
29
+ ]
30
+ dependencies = ["requests>=2.25"]
31
+
32
+ [project.optional-dependencies]
33
+ dev = ["pytest>=7", "requests>=2.25"]
34
+
35
+ [project.urls]
36
+ Homepage = "https://fileboy.io/docs/libraries/python"
37
+ Documentation = "https://fileboy.io/docs/libraries/python"
38
+ Changelog = "https://github.com/CD2/Fileboy/blob/master/packages/python/CHANGELOG.md"
39
+
40
+ [tool.setuptools.packages.find]
41
+ where = ["src"]
42
+
43
+ [tool.setuptools.package-data]
44
+ fileboy = ["py.typed"]
45
+
46
+ [tool.pytest.ini_options]
47
+ testpaths = ["tests"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,93 @@
1
+ """Official Python client for Fileboy (https://fileboy.io).
2
+
3
+ import fileboy
4
+
5
+ fileboy.configure(public_key="YOUR_PUBLIC_KEY")
6
+ fileboy.client().upload_file("report.pdf")
7
+ """
8
+
9
+ import os
10
+ from dataclasses import dataclass, field
11
+ from typing import Any, Optional
12
+
13
+ from . import mime, token, transforms
14
+ from ._version import __version__
15
+ from .client import DEFAULT_API_URL, Client, Uploadable
16
+ from .errors import (
17
+ AuthenticationError,
18
+ BadRequestError,
19
+ ConfigurationError,
20
+ FileboyConnectionError,
21
+ FileboyError,
22
+ FileboyPermissionError,
23
+ InvalidArgumentError,
24
+ NotFoundError,
25
+ RateLimitError,
26
+ ServerError,
27
+ )
28
+ from .file_data import FileData, Video
29
+ from .webhooks import WebhookVerificationError, sign_webhook, verify_webhook
30
+
31
+
32
+ @dataclass
33
+ class Configuration:
34
+ """Settings for the shared client. Unset values fall back to environment variables."""
35
+
36
+ public_key: Optional[str] = field(default_factory=lambda: os.environ.get("FILEBOY_PUBLIC_KEY"))
37
+ private_key: Optional[str] = field(default_factory=lambda: os.environ.get("FILEBOY_PRIVATE_KEY"))
38
+ api_url: str = field(default_factory=lambda: os.environ.get("FILEBOY_API_URL") or DEFAULT_API_URL)
39
+ timeout: float = 60
40
+
41
+
42
+ _configuration: Optional[Configuration] = None
43
+ _client: Optional[Client] = None
44
+
45
+
46
+ def configuration() -> Configuration:
47
+ """The current settings. Created from the environment on first use."""
48
+ global _configuration
49
+ if _configuration is None:
50
+ _configuration = Configuration()
51
+ return _configuration
52
+
53
+
54
+ def configure(**settings: Any) -> Configuration:
55
+ """Set ``public_key``, ``private_key``, ``api_url`` and/or ``timeout`` for :func:`client`."""
56
+ global _client
57
+ config = configuration()
58
+ for key, value in settings.items():
59
+ if not hasattr(config, key):
60
+ raise ConfigurationError(f"Unknown setting {key!r}")
61
+ setattr(config, key, value)
62
+ _client = None
63
+ return config
64
+
65
+
66
+ def client() -> Client:
67
+ """A client built from :func:`configuration`. Memoized, and rebuilt after :func:`configure`."""
68
+ global _client
69
+ if _client is None:
70
+ config = configuration()
71
+ _client = Client(
72
+ public_key=config.public_key or "",
73
+ private_key=config.private_key,
74
+ api_url=config.api_url,
75
+ timeout=config.timeout,
76
+ )
77
+ return _client
78
+
79
+
80
+ def reset() -> None:
81
+ """Forget all settings and the shared client. Mostly useful in tests."""
82
+ global _configuration, _client
83
+ _configuration = None
84
+ _client = None
85
+
86
+
87
+ __all__ = [
88
+ "__version__", "Client", "Configuration", "configuration", "configure", "client", "reset",
89
+ "FileData", "Video", "Uploadable", "DEFAULT_API_URL", "mime", "token", "transforms",
90
+ "FileboyError", "ConfigurationError", "InvalidArgumentError", "FileboyConnectionError",
91
+ "BadRequestError", "AuthenticationError", "FileboyPermissionError", "NotFoundError",
92
+ "RateLimitError", "ServerError", "WebhookVerificationError", "sign_webhook", "verify_webhook",
93
+ ]
@@ -0,0 +1 @@
1
+ __version__ = "0.1.0"