cloudinary 1.45.0__tar.gz → 1.46.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 (98) hide show
  1. cloudinary-1.46.1/LICENSE +21 -0
  2. {cloudinary-1.45.0 → cloudinary-1.46.1}/MANIFEST.in +2 -0
  3. cloudinary-1.46.1/PKG-INFO +221 -0
  4. cloudinary-1.46.1/README.md +150 -0
  5. {cloudinary-1.45.0 → cloudinary-1.46.1}/cloudinary/__init__.py +1 -1
  6. {cloudinary-1.45.0 → cloudinary-1.46.1}/cloudinary/api.py +184 -1
  7. {cloudinary-1.45.0 → cloudinary-1.46.1}/cloudinary/api_client/call_account_api.py +8 -2
  8. {cloudinary-1.45.0 → cloudinary-1.46.1}/cloudinary/api_client/call_api.py +35 -8
  9. {cloudinary-1.45.0 → cloudinary-1.46.1}/cloudinary/api_client/execute_request.py +6 -3
  10. cloudinary-1.46.1/cloudinary/docs/README.md +63 -0
  11. cloudinary-1.46.1/cloudinary/docs/configure-cloudinary.md +86 -0
  12. cloudinary-1.46.1/cloudinary/docs/get-credentials.md +149 -0
  13. cloudinary-1.46.1/cloudinary/docs/import-and-call.md +29 -0
  14. cloudinary-1.46.1/cloudinary/docs/moderate-upload.md +156 -0
  15. cloudinary-1.46.1/cloudinary/docs/platform-capabilities.md +130 -0
  16. cloudinary-1.46.1/cloudinary/docs/search-and-manage-assets.md +189 -0
  17. cloudinary-1.46.1/cloudinary/docs/sign-browser-upload.md +136 -0
  18. cloudinary-1.46.1/cloudinary/docs/transform-and-deliver-image.md +148 -0
  19. cloudinary-1.46.1/cloudinary/docs/transform-and-deliver-video.md +128 -0
  20. cloudinary-1.46.1/cloudinary/docs/troubleshoot-errors.md +175 -0
  21. cloudinary-1.46.1/cloudinary/docs/upload-image.md +127 -0
  22. cloudinary-1.46.1/cloudinary/docs/upload-large-video.md +112 -0
  23. cloudinary-1.46.1/cloudinary/docs/use-structured-metadata.md +144 -0
  24. cloudinary-1.46.1/cloudinary/docs/use-with-django.md +178 -0
  25. {cloudinary-1.45.0 → cloudinary-1.46.1}/cloudinary/provisioning/__init__.py +1 -1
  26. {cloudinary-1.45.0 → cloudinary-1.46.1}/cloudinary/provisioning/account.py +62 -6
  27. {cloudinary-1.45.0 → cloudinary-1.46.1}/cloudinary/uploader.py +17 -3
  28. {cloudinary-1.45.0 → cloudinary-1.46.1}/cloudinary/utils.py +57 -2
  29. cloudinary-1.46.1/cloudinary.egg-info/PKG-INFO +221 -0
  30. {cloudinary-1.45.0 → cloudinary-1.46.1}/cloudinary.egg-info/SOURCES.txt +19 -1
  31. {cloudinary-1.45.0 → cloudinary-1.46.1}/pyproject.toml +7 -4
  32. {cloudinary-1.45.0 → cloudinary-1.46.1}/setup.py +1 -1
  33. {cloudinary-1.45.0 → cloudinary-1.46.1}/test/test_api.py +5 -2
  34. {cloudinary-1.45.0 → cloudinary-1.46.1}/test/test_archive.py +3 -0
  35. cloudinary-1.46.1/test/test_notifications.py +321 -0
  36. {cloudinary-1.45.0 → cloudinary-1.46.1}/test/test_provisioning_api.py +191 -15
  37. cloudinary-1.46.1/test/test_request_id.py +98 -0
  38. cloudinary-1.46.1/test/test_triggers.py +162 -0
  39. {cloudinary-1.45.0 → cloudinary-1.46.1}/test/test_uploader.py +24 -1
  40. cloudinary-1.45.0/LICENSE.txt +0 -5
  41. cloudinary-1.45.0/PKG-INFO +0 -174
  42. cloudinary-1.45.0/README.md +0 -119
  43. cloudinary-1.45.0/cloudinary.egg-info/PKG-INFO +0 -174
  44. {cloudinary-1.45.0 → cloudinary-1.46.1}/cloudinary/api_client/__init__.py +0 -0
  45. {cloudinary-1.45.0 → cloudinary-1.46.1}/cloudinary/api_client/tcp_keep_alive_manager.py +0 -0
  46. {cloudinary-1.45.0 → cloudinary-1.46.1}/cloudinary/auth_token.py +0 -0
  47. {cloudinary-1.45.0 → cloudinary-1.46.1}/cloudinary/cache/__init__.py +0 -0
  48. {cloudinary-1.45.0 → cloudinary-1.46.1}/cloudinary/cache/adapter/__init__.py +0 -0
  49. {cloudinary-1.45.0 → cloudinary-1.46.1}/cloudinary/cache/adapter/cache_adapter.py +0 -0
  50. {cloudinary-1.45.0 → cloudinary-1.46.1}/cloudinary/cache/adapter/key_value_cache_adapter.py +0 -0
  51. {cloudinary-1.45.0 → cloudinary-1.46.1}/cloudinary/cache/responsive_breakpoints_cache.py +0 -0
  52. {cloudinary-1.45.0 → cloudinary-1.46.1}/cloudinary/cache/storage/__init__.py +0 -0
  53. {cloudinary-1.45.0 → cloudinary-1.46.1}/cloudinary/cache/storage/file_system_key_value_storage.py +0 -0
  54. {cloudinary-1.45.0 → cloudinary-1.46.1}/cloudinary/cache/storage/key_value_storage.py +0 -0
  55. {cloudinary-1.45.0 → cloudinary-1.46.1}/cloudinary/compat.py +0 -0
  56. {cloudinary-1.45.0 → cloudinary-1.46.1}/cloudinary/exceptions.py +0 -0
  57. {cloudinary-1.45.0 → cloudinary-1.46.1}/cloudinary/forms.py +0 -0
  58. {cloudinary-1.45.0 → cloudinary-1.46.1}/cloudinary/http_client.py +0 -0
  59. {cloudinary-1.45.0 → cloudinary-1.46.1}/cloudinary/models.py +0 -0
  60. {cloudinary-1.45.0 → cloudinary-1.46.1}/cloudinary/poster/__init__.py +0 -0
  61. {cloudinary-1.45.0 → cloudinary-1.46.1}/cloudinary/poster/encode.py +0 -0
  62. {cloudinary-1.45.0 → cloudinary-1.46.1}/cloudinary/poster/streaminghttp.py +0 -0
  63. {cloudinary-1.45.0 → cloudinary-1.46.1}/cloudinary/provisioning/account_config.py +0 -0
  64. {cloudinary-1.45.0 → cloudinary-1.46.1}/cloudinary/search.py +0 -0
  65. {cloudinary-1.45.0 → cloudinary-1.46.1}/cloudinary/search_folders.py +0 -0
  66. {cloudinary-1.45.0 → cloudinary-1.46.1}/cloudinary/static/cloudinary/html/cloudinary_cors.html +0 -0
  67. {cloudinary-1.45.0 → cloudinary-1.46.1}/cloudinary/static/cloudinary/js/canvas-to-blob.min.js +0 -0
  68. {cloudinary-1.45.0 → cloudinary-1.46.1}/cloudinary/static/cloudinary/js/jquery.cloudinary.js +0 -0
  69. {cloudinary-1.45.0 → cloudinary-1.46.1}/cloudinary/static/cloudinary/js/jquery.fileupload-image.js +0 -0
  70. {cloudinary-1.45.0 → cloudinary-1.46.1}/cloudinary/static/cloudinary/js/jquery.fileupload-process.js +0 -0
  71. {cloudinary-1.45.0 → cloudinary-1.46.1}/cloudinary/static/cloudinary/js/jquery.fileupload-validate.js +0 -0
  72. {cloudinary-1.45.0 → cloudinary-1.46.1}/cloudinary/static/cloudinary/js/jquery.fileupload.js +0 -0
  73. {cloudinary-1.45.0 → cloudinary-1.46.1}/cloudinary/static/cloudinary/js/jquery.iframe-transport.js +0 -0
  74. {cloudinary-1.45.0 → cloudinary-1.46.1}/cloudinary/static/cloudinary/js/jquery.ui.widget.js +0 -0
  75. {cloudinary-1.45.0 → cloudinary-1.46.1}/cloudinary/static/cloudinary/js/load-image.all.min.js +0 -0
  76. {cloudinary-1.45.0 → cloudinary-1.46.1}/cloudinary/templates/cloudinary_direct_upload.html +0 -0
  77. {cloudinary-1.45.0 → cloudinary-1.46.1}/cloudinary/templates/cloudinary_includes.html +0 -0
  78. {cloudinary-1.45.0 → cloudinary-1.46.1}/cloudinary/templates/cloudinary_js_config.html +0 -0
  79. {cloudinary-1.45.0 → cloudinary-1.46.1}/cloudinary/templatetags/__init__.py +0 -0
  80. {cloudinary-1.45.0 → cloudinary-1.46.1}/cloudinary/templatetags/cloudinary.py +0 -0
  81. {cloudinary-1.45.0 → cloudinary-1.46.1}/cloudinary.egg-info/dependency_links.txt +0 -0
  82. {cloudinary-1.45.0 → cloudinary-1.46.1}/cloudinary.egg-info/not-zip-safe +0 -0
  83. {cloudinary-1.45.0 → cloudinary-1.46.1}/cloudinary.egg-info/requires.txt +0 -0
  84. {cloudinary-1.45.0 → cloudinary-1.46.1}/cloudinary.egg-info/top_level.txt +0 -0
  85. {cloudinary-1.45.0 → cloudinary-1.46.1}/setup.cfg +0 -0
  86. {cloudinary-1.45.0 → cloudinary-1.46.1}/test/test_api_authorization.py +0 -0
  87. {cloudinary-1.45.0 → cloudinary-1.46.1}/test/test_auth_token.py +0 -0
  88. {cloudinary-1.45.0 → cloudinary-1.46.1}/test/test_cloudinary_resource.py +0 -0
  89. {cloudinary-1.45.0 → cloudinary-1.46.1}/test/test_config.py +0 -0
  90. {cloudinary-1.45.0 → cloudinary-1.46.1}/test/test_expression_normalization.py +0 -0
  91. {cloudinary-1.45.0 → cloudinary-1.46.1}/test/test_http_client.py +0 -0
  92. {cloudinary-1.45.0 → cloudinary-1.46.1}/test/test_image.py +0 -0
  93. {cloudinary-1.45.0 → cloudinary-1.46.1}/test/test_metadata.py +0 -0
  94. {cloudinary-1.45.0 → cloudinary-1.46.1}/test/test_metadata_rules.py +0 -0
  95. {cloudinary-1.45.0 → cloudinary-1.46.1}/test/test_search.py +0 -0
  96. {cloudinary-1.45.0 → cloudinary-1.46.1}/test/test_streaming_profiles.py +0 -0
  97. {cloudinary-1.45.0 → cloudinary-1.46.1}/test/test_utils.py +0 -0
  98. {cloudinary-1.45.0 → cloudinary-1.46.1}/test/test_video.py +0 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) Cloudinary 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.
@@ -1,4 +1,6 @@
1
1
  include *.txt
2
+ include LICENSE
3
+ recursive-include cloudinary/docs *.md
2
4
  recursive-include cloudinary/templates *.html
3
5
  recursive-include cloudinary/static *.html *.js
4
6
  prune django_tests
@@ -0,0 +1,221 @@
1
+ Metadata-Version: 2.1
2
+ Name: cloudinary
3
+ Version: 1.46.1
4
+ Summary: Upload, transform, optimize, and manage images and videos with Cloudinary from Python or Django.
5
+ Author-email: Cloudinary <info@cloudinary.com>
6
+ License: MIT License
7
+
8
+ Copyright (c) Cloudinary Ltd.
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
28
+ Project-URL: Homepage, https://cloudinary.com
29
+ Project-URL: Source, https://github.com/cloudinary/pycloudinary
30
+ Project-URL: Changelog, https://raw.githubusercontent.com/cloudinary/pycloudinary/master/CHANGELOG.md
31
+ Keywords: cloudinary,image,video,upload,crop,resize,filter,transformation,manipulation,cdn
32
+ Classifier: Development Status :: 5 - Production/Stable
33
+ Classifier: Environment :: Web Environment
34
+ Classifier: Framework :: Django
35
+ Classifier: Framework :: Django :: 1.11
36
+ Classifier: Framework :: Django :: 2.2
37
+ Classifier: Framework :: Django :: 4.2
38
+ Classifier: Framework :: Django :: 5.0
39
+ Classifier: Framework :: Django :: 5.1
40
+ Classifier: Framework :: Django :: 5.2
41
+ Classifier: Framework :: Django :: 6.0
42
+ Classifier: Intended Audience :: Developers
43
+ Classifier: License :: OSI Approved :: MIT License
44
+ Classifier: Programming Language :: Python
45
+ Classifier: Programming Language :: Python :: 2
46
+ Classifier: Programming Language :: Python :: 2.7
47
+ Classifier: Programming Language :: Python :: 3
48
+ Classifier: Programming Language :: Python :: 3.10
49
+ Classifier: Programming Language :: Python :: 3.11
50
+ Classifier: Programming Language :: Python :: 3.12
51
+ Classifier: Programming Language :: Python :: 3.13
52
+ Classifier: Programming Language :: Python :: 3.14
53
+ Classifier: Topic :: Internet :: WWW/HTTP
54
+ Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
55
+ Classifier: Topic :: Multimedia :: Graphics
56
+ Classifier: Topic :: Multimedia :: Graphics :: Graphics Conversion
57
+ Classifier: Topic :: Multimedia :: Sound/Audio
58
+ Classifier: Topic :: Multimedia :: Sound/Audio :: Conversion
59
+ Classifier: Topic :: Multimedia :: Video
60
+ Classifier: Topic :: Multimedia :: Video :: Conversion
61
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
62
+ Description-Content-Type: text/markdown
63
+ License-File: LICENSE
64
+ Requires-Dist: six
65
+ Requires-Dist: urllib3>=1.26.5
66
+ Requires-Dist: certifi
67
+ Provides-Extra: dev
68
+ Requires-Dist: tox; extra == "dev"
69
+ Requires-Dist: pytest==4.6; python_version < "3.7" and extra == "dev"
70
+ Requires-Dist: pytest; python_version >= "3.7" and extra == "dev"
71
+
72
+ # Cloudinary Python SDK
73
+
74
+ Upload, transform, optimize, and manage images and videos with Cloudinary from Python or Django — the `cloudinary` package on PyPI.
75
+
76
+ [![Tests](https://github.com/cloudinary/pycloudinary/actions/workflows/test.yml/badge.svg?branch=master)](https://github.com/cloudinary/pycloudinary/actions/workflows/test.yml)
77
+ [![PyPI](https://img.shields.io/pypi/v/cloudinary.svg)](https://pypi.org/project/cloudinary/)
78
+ [![Downloads](https://img.shields.io/pypi/dm/cloudinary.svg)](https://pypi.org/project/cloudinary/)
79
+ [![License](https://img.shields.io/pypi/l/cloudinary.svg)](LICENSE)
80
+
81
+ ## Install
82
+
83
+ ```bash
84
+ pip install cloudinary
85
+ ```
86
+
87
+ ## Quick start
88
+
89
+ Set your API environment variable (Console > Settings > API Keys):
90
+
91
+ ```bash
92
+ export CLOUDINARY_URL=cloudinary://<api_key>:<api_secret>@<cloud_name>
93
+ ```
94
+
95
+ Upload an image and get an optimized delivery URL:
96
+
97
+ ```python
98
+ import cloudinary
99
+ import cloudinary.uploader # cloudinary.uploader must be imported explicitly
100
+
101
+ # Configuration is read from CLOUDINARY_URL automatically.
102
+
103
+
104
+ def main():
105
+ # Upload a remote image (a local file path works the same way)
106
+ result = cloudinary.uploader.upload(
107
+ "https://res.cloudinary.com/demo/image/upload/sample.jpg",
108
+ public_id="quickstart-sample",
109
+ )
110
+ print("Uploaded: {0}".format(result["public_id"]))
111
+
112
+ # Build a 400x400 auto-cropped URL with automatic format and quality
113
+ url = cloudinary.CloudinaryImage(result["public_id"]).build_url(
114
+ width=400,
115
+ height=400,
116
+ crop="fill",
117
+ gravity="auto",
118
+ fetch_format="auto",
119
+ quality="auto",
120
+ secure=True,
121
+ )
122
+ print("Optimized URL: {0}".format(url))
123
+
124
+
125
+ if __name__ == "__main__":
126
+ try:
127
+ main()
128
+ except Exception as error:
129
+ print("Quick start failed: {0}".format(error))
130
+ print("Check that CLOUDINARY_URL is set (Console > Settings > API Keys).")
131
+ raise SystemExit(1)
132
+ ```
133
+
134
+ Save as `quickstart.py` and run `python quickstart.py`. [Create a free account](https://cloudinary.com/users/register_free) if you don't have one — or call `cloudinary.provisioning.create_cloud()` to [provision one without signing up](cloudinary/docs/get-credentials.md).
135
+
136
+ ## Common tasks
137
+
138
+ - [Get Cloudinary credentials](cloudinary/docs/get-credentials.md)
139
+ - [Upload an image](cloudinary/docs/upload-image.md)
140
+ - [Upload a large video](cloudinary/docs/upload-large-video.md)
141
+ - [Sign a browser upload](cloudinary/docs/sign-browser-upload.md)
142
+ - [Transform and deliver an image](cloudinary/docs/transform-and-deliver-image.md)
143
+ - [Transform and deliver a video](cloudinary/docs/transform-and-deliver-video.md)
144
+ - [Search and manage assets](cloudinary/docs/search-and-manage-assets.md)
145
+ - [Moderate an upload](cloudinary/docs/moderate-upload.md)
146
+ - [Use structured metadata](cloudinary/docs/use-structured-metadata.md)
147
+ - [Use with Django](cloudinary/docs/use-with-django.md)
148
+ - [Troubleshoot errors](cloudinary/docs/troubleshoot-errors.md)
149
+
150
+ Runnable versions live in [`examples/`](examples/) — each is a complete file you can run directly.
151
+
152
+ ## When to use this SDK
153
+
154
+ Use this package in **Python server-side code**, including Django: uploads, signed
155
+ operations, asset administration, search, moderation, and delivery URL generation.
156
+
157
+ For other jobs, better-fitting tools exist:
158
+
159
+ - Browser or frontend framework rendering: the [frontend SDKs](https://cloudinary.com/documentation/frontend_sdks) ([md](https://cloudinary.com/documentation/frontend_sdks.md)).
160
+ - Complete in-browser upload UI: [Upload Widget](https://cloudinary.com/documentation/upload_widget) ([md](https://cloudinary.com/documentation/upload_widget.md)).
161
+ - Text-to-image generation and image-to-video: [platform APIs](https://cloudinary.com/documentation/image_generation_addon) ([md](https://cloudinary.com/documentation/image_generation_addon.md)), not wrapped by this package.
162
+ - Multi-step media workflow automation: [MediaFlows](https://cloudinary.com/documentation/mediaflows_user_guide) ([md](https://cloudinary.com/documentation/mediaflows_user_guide.md)).
163
+ - Interactive agent-driven asset operations: [Cloudinary MCP servers and Skills](https://cloudinary.com/documentation/cloudinary_llm_mcp) ([md](https://cloudinary.com/documentation/cloudinary_llm_mcp.md)).
164
+
165
+ The full capability map — plus the Skills, MCP servers, and CLI worth setting up first —
166
+ is in [cloudinary/docs/platform-capabilities.md](cloudinary/docs/platform-capabilities.md).
167
+
168
+ ## Status and compatibility
169
+
170
+ Stable, actively maintained. See [CHANGELOG.md](CHANGELOG.md).
171
+
172
+ | SDK version | Python | Django |
173
+ |-------------|--------|--------|
174
+ | 1.x | 3.10 – 3.14 | 4.2, 5.0, 5.1, 5.2, 6.0 |
175
+
176
+ The table lists what CI tests on every commit. Older Python and Django releases are
177
+ supported on a best-effort basis.
178
+
179
+ ## Documentation
180
+
181
+ - [Bundled task docs](cloudinary/docs/README.md) — ship inside the package, version-matched.
182
+ - [Python and Django SDK guide](https://cloudinary.com/documentation/django_integration) — the full documentation ([md](https://cloudinary.com/documentation/django_integration.md)).
183
+ - [Python quick start](https://cloudinary.com/documentation/python_quickstart) — a short end-to-end walkthrough ([md](https://cloudinary.com/documentation/python_quickstart.md)).
184
+ - [Transformation and REST API references](https://cloudinary.com/documentation/cloudinary_references) — syntax and examples for all SDKs ([md](https://cloudinary.com/documentation/cloudinary_references.md)).
185
+
186
+ Documentation links in this README point at the browsable HTML page, with an `(md)`
187
+ companion link that returns the same page as raw Markdown. Inside `cloudinary/docs/` and
188
+ `examples/` the links are Markdown-only, since those files are written to be read by
189
+ coding agents. Either form works for any page: add `.md` for Markdown, drop it for HTML.
190
+
191
+ ## For AI coding agents
192
+
193
+ - Contributing to this repo: read [AGENTS.md](AGENTS.md).
194
+ - Using the installed package: the Markdown docs in the installed package's
195
+ `cloudinary/docs/` directory match your installed version and are the source of truth.
196
+ Locate them with
197
+ `python -c "import cloudinary, pathlib; print(pathlib.Path(cloudinary.__file__).parent / 'docs')"`,
198
+ and start with [platform-capabilities](cloudinary/docs/platform-capabilities.md) before
199
+ assuming a feature exists.
200
+
201
+ ## Support
202
+
203
+ - SDK bugs and feature requests: [GitHub issues](https://github.com/cloudinary/pycloudinary/issues)
204
+ - Account and platform questions: [Cloudinary support](https://support.cloudinary.com)
205
+ - Sample projects: [`samples/`](samples/) and the
206
+ [Django Photo Album](https://github.com/cloudinary/cloudinary-django-sample)
207
+ - Contributing: [CONTRIBUTING.md](CONTRIBUTING.md)
208
+
209
+ More resources: [Cloudinary Academy](https://training.cloudinary.com/) for courses,
210
+ [code explorers and feature demos](https://cloudinary.com/documentation/code_explorers_demos_index),
211
+ and the [Cloudinary roadmap](https://cloudinary.com/roadmap).
212
+
213
+ ## Security
214
+
215
+ See [SECURITY.md](SECURITY.md) for private vulnerability reporting. Keep your
216
+ `api_secret` in server-side code; for client uploads, use the server-signed pattern in
217
+ [Sign a browser upload](cloudinary/docs/sign-browser-upload.md).
218
+
219
+ ## License
220
+
221
+ Released under the MIT license — see [LICENSE](LICENSE). Copyright (c) Cloudinary Ltd.
@@ -0,0 +1,150 @@
1
+ # Cloudinary Python SDK
2
+
3
+ Upload, transform, optimize, and manage images and videos with Cloudinary from Python or Django — the `cloudinary` package on PyPI.
4
+
5
+ [![Tests](https://github.com/cloudinary/pycloudinary/actions/workflows/test.yml/badge.svg?branch=master)](https://github.com/cloudinary/pycloudinary/actions/workflows/test.yml)
6
+ [![PyPI](https://img.shields.io/pypi/v/cloudinary.svg)](https://pypi.org/project/cloudinary/)
7
+ [![Downloads](https://img.shields.io/pypi/dm/cloudinary.svg)](https://pypi.org/project/cloudinary/)
8
+ [![License](https://img.shields.io/pypi/l/cloudinary.svg)](LICENSE)
9
+
10
+ ## Install
11
+
12
+ ```bash
13
+ pip install cloudinary
14
+ ```
15
+
16
+ ## Quick start
17
+
18
+ Set your API environment variable (Console > Settings > API Keys):
19
+
20
+ ```bash
21
+ export CLOUDINARY_URL=cloudinary://<api_key>:<api_secret>@<cloud_name>
22
+ ```
23
+
24
+ Upload an image and get an optimized delivery URL:
25
+
26
+ ```python
27
+ import cloudinary
28
+ import cloudinary.uploader # cloudinary.uploader must be imported explicitly
29
+
30
+ # Configuration is read from CLOUDINARY_URL automatically.
31
+
32
+
33
+ def main():
34
+ # Upload a remote image (a local file path works the same way)
35
+ result = cloudinary.uploader.upload(
36
+ "https://res.cloudinary.com/demo/image/upload/sample.jpg",
37
+ public_id="quickstart-sample",
38
+ )
39
+ print("Uploaded: {0}".format(result["public_id"]))
40
+
41
+ # Build a 400x400 auto-cropped URL with automatic format and quality
42
+ url = cloudinary.CloudinaryImage(result["public_id"]).build_url(
43
+ width=400,
44
+ height=400,
45
+ crop="fill",
46
+ gravity="auto",
47
+ fetch_format="auto",
48
+ quality="auto",
49
+ secure=True,
50
+ )
51
+ print("Optimized URL: {0}".format(url))
52
+
53
+
54
+ if __name__ == "__main__":
55
+ try:
56
+ main()
57
+ except Exception as error:
58
+ print("Quick start failed: {0}".format(error))
59
+ print("Check that CLOUDINARY_URL is set (Console > Settings > API Keys).")
60
+ raise SystemExit(1)
61
+ ```
62
+
63
+ Save as `quickstart.py` and run `python quickstart.py`. [Create a free account](https://cloudinary.com/users/register_free) if you don't have one — or call `cloudinary.provisioning.create_cloud()` to [provision one without signing up](cloudinary/docs/get-credentials.md).
64
+
65
+ ## Common tasks
66
+
67
+ - [Get Cloudinary credentials](cloudinary/docs/get-credentials.md)
68
+ - [Upload an image](cloudinary/docs/upload-image.md)
69
+ - [Upload a large video](cloudinary/docs/upload-large-video.md)
70
+ - [Sign a browser upload](cloudinary/docs/sign-browser-upload.md)
71
+ - [Transform and deliver an image](cloudinary/docs/transform-and-deliver-image.md)
72
+ - [Transform and deliver a video](cloudinary/docs/transform-and-deliver-video.md)
73
+ - [Search and manage assets](cloudinary/docs/search-and-manage-assets.md)
74
+ - [Moderate an upload](cloudinary/docs/moderate-upload.md)
75
+ - [Use structured metadata](cloudinary/docs/use-structured-metadata.md)
76
+ - [Use with Django](cloudinary/docs/use-with-django.md)
77
+ - [Troubleshoot errors](cloudinary/docs/troubleshoot-errors.md)
78
+
79
+ Runnable versions live in [`examples/`](examples/) — each is a complete file you can run directly.
80
+
81
+ ## When to use this SDK
82
+
83
+ Use this package in **Python server-side code**, including Django: uploads, signed
84
+ operations, asset administration, search, moderation, and delivery URL generation.
85
+
86
+ For other jobs, better-fitting tools exist:
87
+
88
+ - Browser or frontend framework rendering: the [frontend SDKs](https://cloudinary.com/documentation/frontend_sdks) ([md](https://cloudinary.com/documentation/frontend_sdks.md)).
89
+ - Complete in-browser upload UI: [Upload Widget](https://cloudinary.com/documentation/upload_widget) ([md](https://cloudinary.com/documentation/upload_widget.md)).
90
+ - Text-to-image generation and image-to-video: [platform APIs](https://cloudinary.com/documentation/image_generation_addon) ([md](https://cloudinary.com/documentation/image_generation_addon.md)), not wrapped by this package.
91
+ - Multi-step media workflow automation: [MediaFlows](https://cloudinary.com/documentation/mediaflows_user_guide) ([md](https://cloudinary.com/documentation/mediaflows_user_guide.md)).
92
+ - Interactive agent-driven asset operations: [Cloudinary MCP servers and Skills](https://cloudinary.com/documentation/cloudinary_llm_mcp) ([md](https://cloudinary.com/documentation/cloudinary_llm_mcp.md)).
93
+
94
+ The full capability map — plus the Skills, MCP servers, and CLI worth setting up first —
95
+ is in [cloudinary/docs/platform-capabilities.md](cloudinary/docs/platform-capabilities.md).
96
+
97
+ ## Status and compatibility
98
+
99
+ Stable, actively maintained. See [CHANGELOG.md](CHANGELOG.md).
100
+
101
+ | SDK version | Python | Django |
102
+ |-------------|--------|--------|
103
+ | 1.x | 3.10 – 3.14 | 4.2, 5.0, 5.1, 5.2, 6.0 |
104
+
105
+ The table lists what CI tests on every commit. Older Python and Django releases are
106
+ supported on a best-effort basis.
107
+
108
+ ## Documentation
109
+
110
+ - [Bundled task docs](cloudinary/docs/README.md) — ship inside the package, version-matched.
111
+ - [Python and Django SDK guide](https://cloudinary.com/documentation/django_integration) — the full documentation ([md](https://cloudinary.com/documentation/django_integration.md)).
112
+ - [Python quick start](https://cloudinary.com/documentation/python_quickstart) — a short end-to-end walkthrough ([md](https://cloudinary.com/documentation/python_quickstart.md)).
113
+ - [Transformation and REST API references](https://cloudinary.com/documentation/cloudinary_references) — syntax and examples for all SDKs ([md](https://cloudinary.com/documentation/cloudinary_references.md)).
114
+
115
+ Documentation links in this README point at the browsable HTML page, with an `(md)`
116
+ companion link that returns the same page as raw Markdown. Inside `cloudinary/docs/` and
117
+ `examples/` the links are Markdown-only, since those files are written to be read by
118
+ coding agents. Either form works for any page: add `.md` for Markdown, drop it for HTML.
119
+
120
+ ## For AI coding agents
121
+
122
+ - Contributing to this repo: read [AGENTS.md](AGENTS.md).
123
+ - Using the installed package: the Markdown docs in the installed package's
124
+ `cloudinary/docs/` directory match your installed version and are the source of truth.
125
+ Locate them with
126
+ `python -c "import cloudinary, pathlib; print(pathlib.Path(cloudinary.__file__).parent / 'docs')"`,
127
+ and start with [platform-capabilities](cloudinary/docs/platform-capabilities.md) before
128
+ assuming a feature exists.
129
+
130
+ ## Support
131
+
132
+ - SDK bugs and feature requests: [GitHub issues](https://github.com/cloudinary/pycloudinary/issues)
133
+ - Account and platform questions: [Cloudinary support](https://support.cloudinary.com)
134
+ - Sample projects: [`samples/`](samples/) and the
135
+ [Django Photo Album](https://github.com/cloudinary/cloudinary-django-sample)
136
+ - Contributing: [CONTRIBUTING.md](CONTRIBUTING.md)
137
+
138
+ More resources: [Cloudinary Academy](https://training.cloudinary.com/) for courses,
139
+ [code explorers and feature demos](https://cloudinary.com/documentation/code_explorers_demos_index),
140
+ and the [Cloudinary roadmap](https://cloudinary.com/roadmap).
141
+
142
+ ## Security
143
+
144
+ See [SECURITY.md](SECURITY.md) for private vulnerability reporting. Keep your
145
+ `api_secret` in server-side code; for client uploads, use the server-signed pattern in
146
+ [Sign a browser upload](cloudinary/docs/sign-browser-upload.md).
147
+
148
+ ## License
149
+
150
+ Released under the MIT license — see [LICENSE](LICENSE). Copyright (c) Cloudinary Ltd.
@@ -38,7 +38,7 @@ CL_BLANK = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAA
38
38
  URI_SCHEME = "cloudinary"
39
39
  API_VERSION = "v1_1"
40
40
 
41
- VERSION = "1.45.0"
41
+ VERSION = "1.46.1"
42
42
 
43
43
  _USER_PLATFORM_DETAILS = "; ".join((platform(), "Python {}".format(python_version())))
44
44
 
@@ -1323,6 +1323,189 @@ def update_streaming_profile(name, **options):
1323
1323
  return call_json_api('PUT', uri, params, **options)
1324
1324
 
1325
1325
 
1326
+ def triggers(**options):
1327
+ """
1328
+ Lists all notification triggers.
1329
+
1330
+ :param options: Additional options.
1331
+ :keyword str event_type: Restricts the list to triggers of a single event type.
1332
+ :return: A dictionary with a "triggers" key and a "total" count.
1333
+ :rtype: Response
1334
+ """
1335
+ params = {"event_type": options.pop("event_type", None)}
1336
+ return call_json_api("get", ["triggers"], params, **options)
1337
+
1338
+
1339
+ def create_trigger(uri, event_type, uri_type=None, **options):
1340
+ """
1341
+ Creates a notification trigger.
1342
+
1343
+ :param uri: The destination of the notification. An https:// webhook URL, or a
1344
+ poll:// destination when uri_type is "poll" - either "poll://<channel>"
1345
+ for a named channel or "poll://*" for an anonymous one addressed by batch_id.
1346
+ :type uri: str
1347
+ :param event_type: The event that fires the trigger. One of:
1348
+ "all", "access_control_changed", "bulk_refresh_auto_fetch",
1349
+ "create_folder", "delete", "delete_by_token", "delete_folder",
1350
+ "eager", "error", "explode", "generate_archive", "info",
1351
+ "invalidate_custom_cdn", "moderation", "moderation_summary",
1352
+ "move", "move_or_rename_asset_folder", "multi", "publish",
1353
+ "rename", "report", "resource_context_changed",
1354
+ "resource_display_name_changed", "resource_metadata_changed",
1355
+ "resource_tags_changed", "restore_asset_version", "sprite",
1356
+ "upload".
1357
+ The server owns this list and may extend it; values are passed
1358
+ through unvalidated.
1359
+ :type event_type: str
1360
+ :param uri_type: How the notification is delivered:
1361
+ - "webhook" (the default): delivers the notification as an HTTP POST
1362
+ to the destination URL.
1363
+ - "flow": dispatches the notification to a Cloudinary flow.
1364
+ - "poll": buffers the notification for retrieval with `notifications`
1365
+ instead of an outbound HTTP request.
1366
+ :type uri_type: str, optional
1367
+ :param options: Additional options.
1368
+ :return: The created trigger.
1369
+ :rtype: Response
1370
+ """
1371
+ params = {"uri": uri, "event_type": event_type, "uri_type": uri_type}
1372
+ return call_json_api("post", ["triggers"], params, **options)
1373
+
1374
+
1375
+ def update_trigger(trigger_id, new_uri, **options):
1376
+ """
1377
+ Updates the destination URI of an existing trigger.
1378
+
1379
+ :param trigger_id: The ID of the trigger to update.
1380
+ :type trigger_id: str
1381
+ :param new_uri: The new destination URI.
1382
+ :type new_uri: str
1383
+ :param options: Additional options.
1384
+ :return: A dictionary with a "message" key.
1385
+ :rtype: Response
1386
+ """
1387
+ return call_json_api("put", ["triggers", trigger_id], {"new_uri": new_uri}, **options)
1388
+
1389
+
1390
+ def delete_trigger(trigger_id, **options):
1391
+ """
1392
+ Deletes a notification trigger.
1393
+
1394
+ :param trigger_id: The ID of the trigger to delete.
1395
+ :type trigger_id: str
1396
+ :param options: Additional options.
1397
+ :return: A dictionary with a "message" key.
1398
+ :rtype: Response
1399
+ """
1400
+ return call_json_api("delete", ["triggers", trigger_id], {}, **options)
1401
+
1402
+
1403
+ def test_trigger(trigger_id, sample_data=None, **options):
1404
+ """
1405
+ Evaluates a trigger's filter against sample data, without delivering a notification.
1406
+
1407
+ :param trigger_id: The ID of the trigger to test.
1408
+ :type trigger_id: str
1409
+ :param sample_data: The sample notification payload to evaluate the filter against.
1410
+ :type sample_data: dict, optional
1411
+ :param options: Additional options.
1412
+ :return: A dictionary with "trigger_id", "filter_present" and "filter_result" keys.
1413
+ :rtype: Response
1414
+ """
1415
+ params = {"sample_data": sample_data}
1416
+ return call_json_api("post", ["triggers", trigger_id, "test"], params, **options)
1417
+
1418
+
1419
+ def notifications(channel=None, batch_id=None, max_messages=None, wait_seconds=None,
1420
+ visibility_timeout=None, **options):
1421
+ """
1422
+ Gets notifications buffered for a poll destination.
1423
+
1424
+ Long-polls exactly one addressing dimension - a named channel or a batch_id. Holds the
1425
+ connection up to wait_seconds, returning as soon as messages are claimed. A claimed
1426
+ message stays invisible for visibility_timeout seconds and is then redelivered, unless
1427
+ it is acknowledged with `ack_notifications` first.
1428
+
1429
+ Each message carries the parsed notification as "payload" and the byte-exact signed
1430
+ bytes, base64url encoded, as "signed_payload". Verify signatures against
1431
+ signed_payload, never against a re-serialization of payload::
1432
+
1433
+ raw = cloudinary.utils.base64url_decode(message["signed_payload"])
1434
+ cloudinary.utils.verify_notification_signature(
1435
+ raw, message["timestamp"], message["signature"])
1436
+
1437
+ :param channel: The named channel to drain ("poll://<channel>" destinations).
1438
+ Mutually exclusive with batch_id.
1439
+ :type channel: str, optional
1440
+ :param batch_id: The batch to drain ("poll://*" anonymous destinations).
1441
+ Mutually exclusive with channel.
1442
+ :type batch_id: str, optional
1443
+ :param max_messages: The maximum number of messages to return. 1-100, default 10.
1444
+ :type max_messages: int, optional
1445
+ :param wait_seconds: The maximum seconds to hold the long-poll open when empty.
1446
+ 0-60, default 20.
1447
+ :type wait_seconds: int, optional
1448
+ :param visibility_timeout: The seconds a claimed message stays invisible before
1449
+ redelivery. 1-600, default 30.
1450
+ :type visibility_timeout: int, optional
1451
+ :param options: Additional options.
1452
+ :keyword int timeout: The HTTP timeout, also configurable via `cloudinary.config(timeout=...)`.
1453
+ When set, it must exceed wait_seconds, or the long poll is cut off
1454
+ client-side before the server answers.
1455
+ :return: Zero or more claimed messages, as {"messages": [...]}. The "messages" key is
1456
+ absent when nothing is buffered - read it with .get("messages", []).
1457
+ :rtype: Response
1458
+ :raises ValueError: If neither or both of channel and batch_id are given.
1459
+ """
1460
+ if bool(channel) == bool(batch_id):
1461
+ raise ValueError("Supply exactly one of 'channel' or 'batch_id'")
1462
+
1463
+ params = {
1464
+ "channel": channel,
1465
+ "batch_id": batch_id,
1466
+ "max_messages": max_messages,
1467
+ "wait_seconds": wait_seconds,
1468
+ "visibility_timeout": visibility_timeout,
1469
+ }
1470
+
1471
+ return __call_notifications_api("get", ["messages"], params, **options)
1472
+
1473
+
1474
+ def ack_notifications(receipt_handles, **options):
1475
+ """
1476
+ Acknowledges claimed notifications, deleting them from the buffer.
1477
+
1478
+ Unacknowledged notifications are redelivered once their visibility timeout expires.
1479
+
1480
+ :param receipt_handles: The receipt handles of the notifications to acknowledge, as
1481
+ returned by `notifications`. A single handle may be passed as a
1482
+ string. Maximum 100 per call.
1483
+ :type receipt_handles: list[str] or str
1484
+ :param options: Additional options.
1485
+ :return: The result of the call.
1486
+ :rtype: Response
1487
+ """
1488
+ if isinstance(receipt_handles, string_types):
1489
+ receipt_handles = [receipt_handles]
1490
+
1491
+ return __call_notifications_api("post", ["messages", "ack"],
1492
+ {"receipt_handles": receipt_handles}, **options)
1493
+
1494
+
1495
+ def __call_notifications_api(method, uri, params, **options):
1496
+ """
1497
+ Private function that assists with performing an API call to the notifications module.
1498
+
1499
+ :param method: The HTTP method. Valid methods: get, post, put, delete
1500
+ :param uri: REST endpoint of the API (without 'notifications' or the cloud name)
1501
+ :param params: Query/body parameters passed to the method
1502
+ :param options: Additional options
1503
+ :rtype: Response
1504
+ :internal
1505
+ """
1506
+ return _call_v2_api(method, uri, params, module="notifications", **options)
1507
+
1508
+
1326
1509
  def only(source, *keys):
1327
1510
  """
1328
1511
  Returns a dictionary containing only the specified keys from the source.
@@ -1384,7 +1567,7 @@ def __delete_resource_params(options, **params):
1384
1567
  :internal
1385
1568
  """
1386
1569
  p = dict(transformations=utils.build_eager(options.get('transformations')),
1387
- **only(options, "keep_original", "next_cursor", "invalidate"))
1570
+ **only(options, "keep_original", "next_cursor", "invalidate", "batch_id"))
1388
1571
  p.update(params)
1389
1572
  return p
1390
1573
 
@@ -1,7 +1,7 @@
1
1
  import cloudinary
2
2
  from cloudinary.api_client.execute_request import execute_request
3
3
  from cloudinary.provisioning.account_config import account_config
4
- from cloudinary.utils import get_http_connector, normalize_params
4
+ from cloudinary.utils import get_http_connector, json_body, normalize_params
5
5
 
6
6
  PROVISIONING_SUB_PATH = "provisioning"
7
7
  ACCOUNT_SUB_PATH = "accounts"
@@ -46,9 +46,15 @@ def _execute_account_request(method, uri, auth, params=None, headers=None, **opt
46
46
  api_version = options.pop("api_version", cloudinary.API_VERSION)
47
47
  provisioning_api_url = "/".join([prefix, api_version, PROVISIONING_SUB_PATH] + uri)
48
48
 
49
+ params = normalize_params(params)
50
+
51
+ if method.upper() != "GET":
52
+ options["body"], headers = json_body(params, headers)
53
+ params = None
54
+
49
55
  return execute_request(http_connector=_http,
50
56
  method=method,
51
- params=normalize_params(params),
57
+ params=params,
52
58
  headers=headers,
53
59
  auth=auth,
54
60
  api_url=provisioning_api_url,