getstream 7.0.0__tar.gz → 7.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.
Files changed (110) hide show
  1. {getstream-7.0.0 → getstream-7.1.0}/.release-please-manifest.json +1 -1
  2. {getstream-7.0.0 → getstream-7.1.0}/CHANGELOG.md +7 -0
  3. {getstream-7.0.0 → getstream-7.1.0}/PKG-INFO +13 -1
  4. {getstream-7.0.0 → getstream-7.1.0}/README.md +12 -0
  5. {getstream-7.0.0 → getstream-7.1.0}/docs/migration-from-stream-chat-python/README.md +1 -0
  6. {getstream-7.0.0 → getstream-7.1.0}/getstream/__init__.py +1 -0
  7. {getstream-7.0.0 → getstream-7.1.0}/getstream/base.py +5 -1
  8. {getstream-7.0.0 → getstream-7.1.0}/getstream/utils/__init__.py +15 -5
  9. {getstream-7.0.0 → getstream-7.1.0}/pyproject.toml +1 -1
  10. {getstream-7.0.0 → getstream-7.1.0}/uv.lock +1 -1
  11. {getstream-7.0.0 → getstream-7.1.0}/.cursor/worktrees.json +0 -0
  12. {getstream-7.0.0 → getstream-7.1.0}/.env.example +0 -0
  13. {getstream-7.0.0 → getstream-7.1.0}/.github/actions/build-rtc-wheel/action.yml +0 -0
  14. {getstream-7.0.0 → getstream-7.1.0}/.github/actions/python-uv-setup/action.yml +0 -0
  15. {getstream-7.0.0 → getstream-7.1.0}/.github/workflows/ci.yml +0 -0
  16. {getstream-7.0.0 → getstream-7.1.0}/.github/workflows/pr_title.yml +0 -0
  17. {getstream-7.0.0 → getstream-7.1.0}/.github/workflows/release.yml +0 -0
  18. {getstream-7.0.0 → getstream-7.1.0}/.github/workflows/rtc_wheels.yml +0 -0
  19. {getstream-7.0.0 → getstream-7.1.0}/.github/workflows/run_integration.yml +0 -0
  20. {getstream-7.0.0 → getstream-7.1.0}/.github/workflows/run_tests.yml +0 -0
  21. {getstream-7.0.0 → getstream-7.1.0}/.github/workflows/scheduled_test.yml +0 -0
  22. {getstream-7.0.0 → getstream-7.1.0}/.github/workflows/stream-py.code-workspace +0 -0
  23. {getstream-7.0.0 → getstream-7.1.0}/.gitignore +0 -0
  24. {getstream-7.0.0 → getstream-7.1.0}/.gitmodules +0 -0
  25. {getstream-7.0.0 → getstream-7.1.0}/.pre-commit-config.yaml +0 -0
  26. {getstream-7.0.0 → getstream-7.1.0}/AGENTS.md +0 -0
  27. {getstream-7.0.0 → getstream-7.1.0}/DEVELOPMENT.md +0 -0
  28. {getstream-7.0.0 → getstream-7.1.0}/LICENSE.md +0 -0
  29. {getstream-7.0.0 → getstream-7.1.0}/MIGRATION_v2_to_v3.md +0 -0
  30. {getstream-7.0.0 → getstream-7.1.0}/MIGRATION_v6_to_v7.md +0 -0
  31. {getstream-7.0.0 → getstream-7.1.0}/Makefile +0 -0
  32. {getstream-7.0.0 → getstream-7.1.0}/dev.py +0 -0
  33. {getstream-7.0.0 → getstream-7.1.0}/docs/migration-from-stream-chat-python/01-setup-and-auth.md +0 -0
  34. {getstream-7.0.0 → getstream-7.1.0}/docs/migration-from-stream-chat-python/02-users.md +0 -0
  35. {getstream-7.0.0 → getstream-7.1.0}/docs/migration-from-stream-chat-python/03-channels.md +0 -0
  36. {getstream-7.0.0 → getstream-7.1.0}/docs/migration-from-stream-chat-python/04-messages-and-reactions.md +0 -0
  37. {getstream-7.0.0 → getstream-7.1.0}/docs/migration-from-stream-chat-python/05-moderation.md +0 -0
  38. {getstream-7.0.0 → getstream-7.1.0}/docs/migration-from-stream-chat-python/06-devices.md +0 -0
  39. {getstream-7.0.0 → getstream-7.1.0}/generate.sh +0 -0
  40. {getstream-7.0.0 → getstream-7.1.0}/generate_webrtc.sh +0 -0
  41. {getstream-7.0.0 → getstream-7.1.0}/getstream/chat/__init__.py +0 -0
  42. {getstream-7.0.0 → getstream-7.1.0}/getstream/chat/async_channel.py +0 -0
  43. {getstream-7.0.0 → getstream-7.1.0}/getstream/chat/async_client.py +0 -0
  44. {getstream-7.0.0 → getstream-7.1.0}/getstream/chat/async_rest_client.py +0 -0
  45. {getstream-7.0.0 → getstream-7.1.0}/getstream/chat/channel.py +0 -0
  46. {getstream-7.0.0 → getstream-7.1.0}/getstream/chat/client.py +0 -0
  47. {getstream-7.0.0 → getstream-7.1.0}/getstream/chat/rest_client.py +0 -0
  48. {getstream-7.0.0 → getstream-7.1.0}/getstream/common/__init__.py +0 -0
  49. {getstream-7.0.0 → getstream-7.1.0}/getstream/common/async_client.py +0 -0
  50. {getstream-7.0.0 → getstream-7.1.0}/getstream/common/async_rest_client.py +0 -0
  51. {getstream-7.0.0 → getstream-7.1.0}/getstream/common/client.py +0 -0
  52. {getstream-7.0.0 → getstream-7.1.0}/getstream/common/rest_client.py +0 -0
  53. {getstream-7.0.0 → getstream-7.1.0}/getstream/common/telemetry.py +0 -0
  54. {getstream-7.0.0 → getstream-7.1.0}/getstream/config.py +0 -0
  55. {getstream-7.0.0 → getstream-7.1.0}/getstream/exceptions.py +0 -0
  56. {getstream-7.0.0 → getstream-7.1.0}/getstream/feeds/__init__.py +0 -0
  57. {getstream-7.0.0 → getstream-7.1.0}/getstream/feeds/client.py +0 -0
  58. {getstream-7.0.0 → getstream-7.1.0}/getstream/feeds/feeds.py +0 -0
  59. {getstream-7.0.0 → getstream-7.1.0}/getstream/feeds/rest_client.py +0 -0
  60. {getstream-7.0.0 → getstream-7.1.0}/getstream/generic.py +0 -0
  61. {getstream-7.0.0 → getstream-7.1.0}/getstream/logging_utils.py +0 -0
  62. {getstream-7.0.0 → getstream-7.1.0}/getstream/meta.py +0 -0
  63. {getstream-7.0.0 → getstream-7.1.0}/getstream/models/__init__.py +0 -0
  64. {getstream-7.0.0 → getstream-7.1.0}/getstream/moderation/__init__.py +0 -0
  65. {getstream-7.0.0 → getstream-7.1.0}/getstream/moderation/async_client.py +0 -0
  66. {getstream-7.0.0 → getstream-7.1.0}/getstream/moderation/async_rest_client.py +0 -0
  67. {getstream-7.0.0 → getstream-7.1.0}/getstream/moderation/client.py +0 -0
  68. {getstream-7.0.0 → getstream-7.1.0}/getstream/moderation/rest_client.py +0 -0
  69. {getstream-7.0.0 → getstream-7.1.0}/getstream/rate_limit.py +0 -0
  70. {getstream-7.0.0 → getstream-7.1.0}/getstream/stream.py +0 -0
  71. {getstream-7.0.0 → getstream-7.1.0}/getstream/stream_response.py +0 -0
  72. {getstream-7.0.0 → getstream-7.1.0}/getstream/tasks.py +0 -0
  73. {getstream-7.0.0 → getstream-7.1.0}/getstream/tests/test_webhook.py +0 -0
  74. {getstream-7.0.0 → getstream-7.1.0}/getstream/utils/event_emitter.py +0 -0
  75. {getstream-7.0.0 → getstream-7.1.0}/getstream/version.py +0 -0
  76. {getstream-7.0.0 → getstream-7.1.0}/getstream/video/__init__.py +0 -0
  77. {getstream-7.0.0 → getstream-7.1.0}/getstream/video/async_call.py +0 -0
  78. {getstream-7.0.0 → getstream-7.1.0}/getstream/video/async_client.py +0 -0
  79. {getstream-7.0.0 → getstream-7.1.0}/getstream/video/async_rest_client.py +0 -0
  80. {getstream-7.0.0 → getstream-7.1.0}/getstream/video/call.py +0 -0
  81. {getstream-7.0.0 → getstream-7.1.0}/getstream/video/client.py +0 -0
  82. {getstream-7.0.0 → getstream-7.1.0}/getstream/video/openai.py +0 -0
  83. {getstream-7.0.0 → getstream-7.1.0}/getstream/video/rest_client.py +0 -0
  84. {getstream-7.0.0 → getstream-7.1.0}/getstream/video/rtc/__init__.py +0 -0
  85. {getstream-7.0.0 → getstream-7.1.0}/getstream/video/rtc/audio_forwarder.py +0 -0
  86. {getstream-7.0.0 → getstream-7.1.0}/getstream/video/rtc/audio_track.py +0 -0
  87. {getstream-7.0.0 → getstream-7.1.0}/getstream/video/rtc/connection_manager.py +0 -0
  88. {getstream-7.0.0 → getstream-7.1.0}/getstream/video/rtc/g711.py +0 -0
  89. {getstream-7.0.0 → getstream-7.1.0}/getstream/video/rtc/participants.py +0 -0
  90. {getstream-7.0.0 → getstream-7.1.0}/getstream/video/rtc/pb/__init__.py +0 -0
  91. {getstream-7.0.0 → getstream-7.1.0}/getstream/video/rtc/pb/stream/__init__.py +0 -0
  92. {getstream-7.0.0 → getstream-7.1.0}/getstream/video/rtc/pb/stream/video/__init__.py +0 -0
  93. {getstream-7.0.0 → getstream-7.1.0}/getstream/video/rtc/pb/stream/video/sfu/__init__.py +0 -0
  94. {getstream-7.0.0 → getstream-7.1.0}/getstream/video/rtc/pb/stream/video/sfu/event/__init__.py +0 -0
  95. {getstream-7.0.0 → getstream-7.1.0}/getstream/video/rtc/pb/stream/video/sfu/event/events_pb2.py +0 -0
  96. {getstream-7.0.0 → getstream-7.1.0}/getstream/video/rtc/pb/stream/video/sfu/event/events_pb2.pyi +0 -0
  97. {getstream-7.0.0 → getstream-7.1.0}/getstream/video/rtc/pb/stream/video/sfu/models/__init__.py +0 -0
  98. {getstream-7.0.0 → getstream-7.1.0}/getstream/video/rtc/pb/stream/video/sfu/models/models_pb2.py +0 -0
  99. {getstream-7.0.0 → getstream-7.1.0}/getstream/video/rtc/pb/stream/video/sfu/models/models_pb2.pyi +0 -0
  100. {getstream-7.0.0 → getstream-7.1.0}/getstream/video/rtc/pb/stream/video/sfu/signal_rpc/__init__.py +0 -0
  101. {getstream-7.0.0 → getstream-7.1.0}/getstream/video/rtc/pb/stream/video/sfu/signal_rpc/signal_pb2.py +0 -0
  102. {getstream-7.0.0 → getstream-7.1.0}/getstream/video/rtc/pb/stream/video/sfu/signal_rpc/signal_pb2.pyi +0 -0
  103. {getstream-7.0.0 → getstream-7.1.0}/getstream/video/rtc/pb/stream/video/sfu/signal_rpc/signal_twirp.py +0 -0
  104. {getstream-7.0.0 → getstream-7.1.0}/getstream/video/rtc/track_util.py +0 -0
  105. {getstream-7.0.0 → getstream-7.1.0}/getstream/video/rtc/tracks.py +0 -0
  106. {getstream-7.0.0 → getstream-7.1.0}/getstream/video/rtc/utils.py +0 -0
  107. {getstream-7.0.0 → getstream-7.1.0}/getstream/video/rtc/video_forwarder.py +0 -0
  108. {getstream-7.0.0 → getstream-7.1.0}/getstream/webhook.py +0 -0
  109. {getstream-7.0.0 → getstream-7.1.0}/pytest.ini +0 -0
  110. {getstream-7.0.0 → getstream-7.1.0}/release-please-config.json +0 -0
@@ -1,4 +1,4 @@
1
1
  {
2
- ".": "7.0.0",
2
+ ".": "7.1.0",
3
3
  "getstream-rtc": "0.1.0"
4
4
  }
@@ -7,6 +7,13 @@ Entries from 6.1.1 on are generated by release-please from commit messages; earl
7
7
  ones were written by hand in the [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
8
8
  format.
9
9
 
10
+ ## [7.1.0](https://github.com/GetStream/stream-py/compare/v7.0.0...v7.1.0) (2026-11-04)
11
+
12
+
13
+ ### Features
14
+
15
+ * add NULL to send an explicit null in request bodies ([#314](https://github.com/GetStream/stream-py/issues/314)) ([955535e](https://github.com/GetStream/stream-py/commit/955535ed73d2016ccffc7924ed7c0ce60c641284))
16
+
10
17
  ## [7.0.0](https://github.com/GetStream/stream-py/compare/v6.1.1...v7.0.0) (2026-10-22)
11
18
 
12
19
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: getstream
3
- Version: 7.0.0
3
+ Version: 7.1.0
4
4
  Summary: GetStream Python SDK - Build scalable activity feeds, chat, and video calling applications
5
5
  Author-email: sachaarbonel <sacha.arbonel@hotmail.fr>, tbarbugli <tbarbugli@gmail.com>
6
6
  License-File: LICENSE.md
@@ -186,6 +186,18 @@ client = Stream(api_key=..., api_secret=..., retry=RetryConfig(enabled=True, max
186
186
 
187
187
  Only idempotent `GET`/`HEAD` requests are retried, and only on HTTP 429 (unless the backend marked it unrecoverable) or a transport-level failure (timeout, connection reset, DNS, TLS). A 429's `Retry-After` header is honored (clamped to `max_backoff`); otherwise the delay uses full jitter over an exponential backoff. A retried failure logs `http.request.failed` at DEBUG; a final, non-retried failure logs it at ERROR (or not at all for a final 429, since that's already covered by `http.response.received`).
188
188
 
189
+ ### Sending null
190
+
191
+ Request bodies drop `None` values inside objects, so an unset optional field never overwrites stored data. `None` items in a list are kept and sent as `null`. To store an explicit `null`, for example inside custom data, pass `NULL`:
192
+
193
+ ```python
194
+ from getstream import NULL
195
+
196
+ channel.update_channel_partial(set={"expires_at": NULL})
197
+ ```
198
+
199
+ To remove a field instead, use `unset`, for example `unset=["expires_at"]`.
200
+
189
201
  ### App configuration
190
202
 
191
203
  ```python
@@ -143,6 +143,18 @@ client = Stream(api_key=..., api_secret=..., retry=RetryConfig(enabled=True, max
143
143
 
144
144
  Only idempotent `GET`/`HEAD` requests are retried, and only on HTTP 429 (unless the backend marked it unrecoverable) or a transport-level failure (timeout, connection reset, DNS, TLS). A 429's `Retry-After` header is honored (clamped to `max_backoff`); otherwise the delay uses full jitter over an exponential backoff. A retried failure logs `http.request.failed` at DEBUG; a final, non-retried failure logs it at ERROR (or not at all for a final 429, since that's already covered by `http.response.received`).
145
145
 
146
+ ### Sending null
147
+
148
+ Request bodies drop `None` values inside objects, so an unset optional field never overwrites stored data. `None` items in a list are kept and sent as `null`. To store an explicit `null`, for example inside custom data, pass `NULL`:
149
+
150
+ ```python
151
+ from getstream import NULL
152
+
153
+ channel.update_channel_partial(set={"expires_at": NULL})
154
+ ```
155
+
156
+ To remove a field instead, use `unset`, for example `unset=["expires_at"]`.
157
+
146
158
  ### App configuration
147
159
 
148
160
  ```python
@@ -20,6 +20,7 @@
20
20
  | Custom fields | Top-level dict keys | Nested in `custom` dict |
21
21
  | Response access | Dict keys (`response["users"]`) | Typed properties (`response.data.users`) |
22
22
  | Env vars | `STREAM_CHAT_TIMEOUT` | `STREAM_API_KEY`, `STREAM_API_SECRET`, `STREAM_TIMEOUT` |
23
+ | `None` in request data | Sent as `null` | Dropped from objects, kept in lists; pass `getstream.NULL` to send `null` |
23
24
 
24
25
  ## Quick Example
25
26
 
@@ -1,5 +1,6 @@
1
1
  import logging
2
2
 
3
+ from getstream.utils import NULL # noqa: F401
3
4
  from getstream.config import RetryConfig # noqa: F401
4
5
  from getstream.exceptions import ( # noqa: F401
5
6
  StreamApiException,
@@ -22,6 +22,7 @@ from getstream.generic import T
22
22
  import httpx
23
23
  from getstream.config import BaseConfig
24
24
  from getstream.version import VERSION
25
+ from getstream.utils import NULL
25
26
  from urllib.parse import quote
26
27
  from abc import ABC
27
28
  from getstream.common.telemetry import (
@@ -144,7 +145,10 @@ def _read_file_bytes(file_path: str) -> bytes:
144
145
 
145
146
  def _strip_none(obj):
146
147
  """Recursively remove None values from dicts so unset optional fields
147
- are omitted from the JSON body instead of being sent as null."""
148
+ are omitted from the JSON body instead of being sent as null. ``NULL``
149
+ becomes null."""
150
+ if obj is NULL:
151
+ return None
148
152
  if isinstance(obj, dict):
149
153
  return {k: _strip_none(v) for k, v in obj.items() if v is not None}
150
154
  if isinstance(obj, list):
@@ -1,3 +1,4 @@
1
+ import enum
1
2
  import json
2
3
  import asyncio
3
4
  from typing import Dict, List, Optional, Union, cast
@@ -36,18 +37,27 @@ def validate_and_clean_url(url):
36
37
  return cleaned_url
37
38
 
38
39
 
39
- def encode_datetime(date: Optional[datetime]) -> Optional[str]:
40
+ # An Enum member because dataclasses-json deep-copies dict values, and a deep copy of an Enum member is the member itself. Its value None makes JSON encoders write null.
41
+ class _Null(enum.Enum):
42
+ NULL = None
43
+
44
+
45
+ NULL = _Null.NULL
46
+ """Sends an explicit JSON ``null``. Request bodies drop ``None`` values inside objects, so use ``NULL`` where ``null`` must reach the API, for example inside custom data."""
47
+
48
+
49
+ def encode_datetime(date: Union[datetime, _Null, None]) -> Union[str, _Null, None]:
40
50
  """
41
51
  Encodes a datetime object into an ISO 8601 formatted string.
42
52
 
43
53
  Args:
44
- date (Optional[datetime]): The datetime object to encode.
54
+ date (Union[datetime, _Null, None]): The datetime object to encode.
45
55
 
46
56
  Returns:
47
- Optional[str]: The ISO 8601 string representation of the datetime, or None if input is None.
57
+ Union[str, _Null, None]: The ISO 8601 string representation of the datetime, or the input unchanged if it is None or NULL.
48
58
  """
49
- if date is None:
50
- return None
59
+ if not isinstance(date, datetime):
60
+ return date
51
61
  return date.isoformat()
52
62
 
53
63
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "getstream"
3
- version = "7.0.0"
3
+ version = "7.1.0"
4
4
  description = "GetStream Python SDK - Build scalable activity feeds, chat, and video calling applications"
5
5
  authors = [
6
6
  { name = "sachaarbonel", email = "sacha.arbonel@hotmail.fr" },
@@ -863,7 +863,7 @@ wheels = [
863
863
 
864
864
  [[package]]
865
865
  name = "getstream"
866
- version = "7.0.0"
866
+ version = "7.1.0"
867
867
  source = { editable = "." }
868
868
  dependencies = [
869
869
  { name = "dataclasses-json" },
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes