linq-python 0.2.2__py3-none-any.whl → 0.2.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
linq/_files.py CHANGED
@@ -3,8 +3,8 @@ from __future__ import annotations
3
3
  import io
4
4
  import os
5
5
  import pathlib
6
- from typing import overload
7
- from typing_extensions import TypeGuard
6
+ from typing import Sequence, cast, overload
7
+ from typing_extensions import TypeVar, TypeGuard
8
8
 
9
9
  import anyio
10
10
 
@@ -17,7 +17,9 @@ from ._types import (
17
17
  HttpxFileContent,
18
18
  HttpxRequestFiles,
19
19
  )
20
- from ._utils import is_tuple_t, is_mapping_t, is_sequence_t
20
+ from ._utils import is_list, is_mapping, is_tuple_t, is_mapping_t, is_sequence_t
21
+
22
+ _T = TypeVar("_T")
21
23
 
22
24
 
23
25
  def is_base64_file_input(obj: object) -> TypeGuard[Base64FileInput]:
@@ -121,3 +123,51 @@ async def async_read_file_content(file: FileContent) -> HttpxFileContent:
121
123
  return await anyio.Path(file).read_bytes()
122
124
 
123
125
  return file
126
+
127
+
128
+ def deepcopy_with_paths(item: _T, paths: Sequence[Sequence[str]]) -> _T:
129
+ """Copy only the containers along the given paths.
130
+
131
+ Used to guard against mutation by extract_files without copying the entire structure.
132
+ Only dicts and lists that lie on a path are copied; everything else
133
+ is returned by reference.
134
+
135
+ For example, given paths=[["foo", "files", "file"]] and the structure:
136
+ {
137
+ "foo": {
138
+ "bar": {"baz": {}},
139
+ "files": {"file": <content>}
140
+ }
141
+ }
142
+ The root dict, "foo", and "files" are copied (they lie on the path).
143
+ "bar" and "baz" are returned by reference (off the path).
144
+ """
145
+ return _deepcopy_with_paths(item, paths, 0)
146
+
147
+
148
+ def _deepcopy_with_paths(item: _T, paths: Sequence[Sequence[str]], index: int) -> _T:
149
+ if not paths:
150
+ return item
151
+ if is_mapping(item):
152
+ key_to_paths: dict[str, list[Sequence[str]]] = {}
153
+ for path in paths:
154
+ if index < len(path):
155
+ key_to_paths.setdefault(path[index], []).append(path)
156
+
157
+ # if no path continues through this mapping, it won't be mutated and copying it is redundant
158
+ if not key_to_paths:
159
+ return item
160
+
161
+ result = dict(item)
162
+ for key, subpaths in key_to_paths.items():
163
+ if key in result:
164
+ result[key] = _deepcopy_with_paths(result[key], subpaths, index + 1)
165
+ return cast(_T, result)
166
+ if is_list(item):
167
+ array_paths = [path for path in paths if index < len(path) and path[index] == "<array>"]
168
+
169
+ # if no path expects a list here, nothing will be mutated inside it - return by reference
170
+ if not array_paths:
171
+ return cast(_T, item)
172
+ return cast(_T, [_deepcopy_with_paths(entry, array_paths, index + 1) for entry in item])
173
+ return item
linq/_utils/__init__.py CHANGED
@@ -24,7 +24,6 @@ from ._utils import (
24
24
  coerce_integer as coerce_integer,
25
25
  file_from_path as file_from_path,
26
26
  strip_not_given as strip_not_given,
27
- deepcopy_minimal as deepcopy_minimal,
28
27
  get_async_library as get_async_library,
29
28
  maybe_coerce_float as maybe_coerce_float,
30
29
  get_required_header as get_required_header,
linq/_utils/_utils.py CHANGED
@@ -86,8 +86,9 @@ def _extract_items(
86
86
  index += 1
87
87
  if is_dict(obj):
88
88
  try:
89
- # We are at the last entry in the path so we must remove the field
90
- if (len(path)) == index:
89
+ # Remove the field if there are no more dict keys in the path,
90
+ # only "<array>" traversal markers or end.
91
+ if all(p == "<array>" for p in path[index:]):
91
92
  item = obj.pop(key)
92
93
  else:
93
94
  item = obj[key]
@@ -176,21 +177,6 @@ def is_iterable(obj: object) -> TypeGuard[Iterable[object]]:
176
177
  return isinstance(obj, Iterable)
177
178
 
178
179
 
179
- def deepcopy_minimal(item: _T) -> _T:
180
- """Minimal reimplementation of copy.deepcopy() that will only copy certain object types:
181
-
182
- - mappings, e.g. `dict`
183
- - list
184
-
185
- This is done for performance reasons.
186
- """
187
- if is_mapping(item):
188
- return cast(_T, {k: deepcopy_minimal(v) for k, v in item.items()})
189
- if is_list(item):
190
- return cast(_T, [deepcopy_minimal(entry) for entry in item])
191
- return item
192
-
193
-
194
180
  # copied from https://github.com/Rapptz/RoboDanny
195
181
  def human_join(seq: Sequence[str], *, delim: str = ", ", final: str = "or") -> str:
196
182
  size = len(seq)
linq/_version.py CHANGED
@@ -1,4 +1,4 @@
1
1
  # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2
2
 
3
3
  __title__ = "linq"
4
- __version__ = "0.2.2" # x-release-please-version
4
+ __version__ = "0.2.4" # x-release-please-version
@@ -219,6 +219,10 @@ class AttachmentsResource(SyncAPIResource):
219
219
  application/x-iwork-keynote-sffkey, application/epub+zip, application/zip,
220
220
  application/x-gzip
221
221
 
222
+ **Transcoded on delivery:**
223
+
224
+ - `audio/x-caf` — CAF files are transcoded to `audio/mp4` for delivery.
225
+
222
226
  **Deprecated (accepted but transcoded):**
223
227
 
224
228
  - `audio/mp3` — Deprecated. Use `audio/mpeg` instead. Files sent as audio/mp3
@@ -490,6 +494,10 @@ class AsyncAttachmentsResource(AsyncAPIResource):
490
494
  application/x-iwork-keynote-sffkey, application/epub+zip, application/zip,
491
495
  application/x-gzip
492
496
 
497
+ **Transcoded on delivery:**
498
+
499
+ - `audio/x-caf` — CAF files are transcoded to `audio/mp4` for delivery.
500
+
493
501
  **Deprecated (accepted but transcoded):**
494
502
 
495
503
  - `audio/mp3` — Deprecated. Use `audio/mpeg` instead. Files sent as audio/mp3
@@ -71,8 +71,20 @@ class TypingResource(SyncAPIResource):
71
71
  """
72
72
  Send a typing indicator to show that someone is typing in the chat.
73
73
 
74
- **Note:** Group chat typing indicators are not currently supported. Attempting
75
- to start a typing indicator in a group chat will return a `403` error.
74
+ ## Behavior & Limitations
75
+
76
+ Typing indicators are best-effort signals with the following limitations:
77
+
78
+ - **Active conversations only:** The recipient must have sent or received a
79
+ message in this chat within the **last 5 minutes**. If the chat is inactive,
80
+ the request is still accepted (`204`) but the indicator will not reach the
81
+ recipient's device.
82
+
83
+ - **No delivery guarantee:** Even for active chats, a `204` response only
84
+ indicates the request was accepted for processing.
85
+
86
+ - **Group chats not supported:** Attempting to start a typing indicator in a
87
+ group chat will return a `403` error.
76
88
 
77
89
  Args:
78
90
  extra_headers: Send extra headers
@@ -108,11 +120,12 @@ class TypingResource(SyncAPIResource):
108
120
  """
109
121
  Stop the typing indicator for the chat.
110
122
 
111
- **Note:** Typing indicators are automatically stopped when a message is sent, so
112
- calling this endpoint after sending a message is unnecessary.
123
+ Typing indicators are automatically stopped when a message is sent, so calling
124
+ this endpoint after sending a message is unnecessary.
125
+
126
+ See the `POST` endpoint above for behavior details and limitations.
113
127
 
114
- **Note:** Group chat typing indicators are not currently supported. Attempting
115
- to stop a typing indicator in a group chat will return a `403` error.
128
+ **Note:** Group chats are not supported and will return a `403` error.
116
129
 
117
130
  Args:
118
131
  extra_headers: Send extra headers
@@ -187,8 +200,20 @@ class AsyncTypingResource(AsyncAPIResource):
187
200
  """
188
201
  Send a typing indicator to show that someone is typing in the chat.
189
202
 
190
- **Note:** Group chat typing indicators are not currently supported. Attempting
191
- to start a typing indicator in a group chat will return a `403` error.
203
+ ## Behavior & Limitations
204
+
205
+ Typing indicators are best-effort signals with the following limitations:
206
+
207
+ - **Active conversations only:** The recipient must have sent or received a
208
+ message in this chat within the **last 5 minutes**. If the chat is inactive,
209
+ the request is still accepted (`204`) but the indicator will not reach the
210
+ recipient's device.
211
+
212
+ - **No delivery guarantee:** Even for active chats, a `204` response only
213
+ indicates the request was accepted for processing.
214
+
215
+ - **Group chats not supported:** Attempting to start a typing indicator in a
216
+ group chat will return a `403` error.
192
217
 
193
218
  Args:
194
219
  extra_headers: Send extra headers
@@ -224,11 +249,12 @@ class AsyncTypingResource(AsyncAPIResource):
224
249
  """
225
250
  Stop the typing indicator for the chat.
226
251
 
227
- **Note:** Typing indicators are automatically stopped when a message is sent, so
228
- calling this endpoint after sending a message is unnecessary.
252
+ Typing indicators are automatically stopped when a message is sent, so calling
253
+ this endpoint after sending a message is unnecessary.
254
+
255
+ See the `POST` endpoint above for behavior details and limitations.
229
256
 
230
- **Note:** Group chat typing indicators are not currently supported. Attempting
231
- to stop a typing indicator in a group chat will return a `403` error.
257
+ **Note:** Group chats are not supported and will return a `403` error.
232
258
 
233
259
  Args:
234
260
  extra_headers: Send extra headers
@@ -34,6 +34,10 @@ class AttachmentCreateParams(TypedDict, total=False):
34
34
  application/x-iwork-keynote-sffkey, application/epub+zip, application/zip,
35
35
  application/x-gzip
36
36
 
37
+ **Transcoded on delivery:**
38
+
39
+ - `audio/x-caf` — CAF files are transcoded to `audio/mp4` for delivery.
40
+
37
41
  **Deprecated (accepted but transcoded):**
38
42
 
39
43
  - `audio/mp3` — Deprecated. Use `audio/mpeg` instead. Files sent as audio/mp3
@@ -38,6 +38,10 @@ class AttachmentRetrieveResponse(BaseModel):
38
38
  application/x-iwork-keynote-sffkey, application/epub+zip, application/zip,
39
39
  application/x-gzip
40
40
 
41
+ **Transcoded on delivery:**
42
+
43
+ - `audio/x-caf` — CAF files are transcoded to `audio/mp4` for delivery.
44
+
41
45
  **Deprecated (accepted but transcoded):**
42
46
 
43
47
  - `audio/mp3` — Deprecated. Use `audio/mpeg` instead. Files sent as audio/mp3
@@ -24,6 +24,9 @@ class SentMessage(BaseModel):
24
24
  id: str
25
25
  """Message identifier (UUID)"""
26
26
 
27
+ created_at: datetime
28
+ """When the message was created"""
29
+
27
30
  delivery_status: Literal["pending", "queued", "sent", "delivered", "failed"]
28
31
  """Current delivery status of a message"""
29
32
 
@@ -33,8 +36,8 @@ class SentMessage(BaseModel):
33
36
  parts: List[Part]
34
37
  """Message parts in order (text, media, and link)"""
35
38
 
36
- sent_at: datetime
37
- """When the message was sent"""
39
+ sent_at: Optional[datetime] = None
40
+ """When the message was actually sent (null if still queued)"""
38
41
 
39
42
  delivered_at: Optional[datetime] = None
40
43
  """When the message was delivered"""
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: linq-python
3
- Version: 0.2.2
3
+ Version: 0.2.4
4
4
  Summary: The official Python library for the linq-api-v3 API
5
5
  Project-URL: Homepage, https://github.com/linq-team/linq-python
6
6
  Project-URL: Repository, https://github.com/linq-team/linq-python
@@ -4,17 +4,17 @@ linq/_client.py,sha256=Z0Zu3KsWLftXW6xJuFeYWKqMMIpbJY6Wx8E_L_j7pSs,101591
4
4
  linq/_compat.py,sha256=_9guQfzYnL3DNtudX5W7T2cdSskx89B5AFfhPQDxMUk,6811
5
5
  linq/_constants.py,sha256=S14PFzyN9-I31wiV7SmIlL5Ga0MLHxdvegInGdXH7tM,462
6
6
  linq/_exceptions.py,sha256=HnBxvnHmvIRYibRyy9Nk3NdBifvtWsqV8Zj-hD0F0QM,3226
7
- linq/_files.py,sha256=KnEzGi_O756MvKyJ4fOCW_u3JhOeWPQ4RsmDvqihDQU,3545
7
+ linq/_files.py,sha256=GsGgJfC6PEiO0d4ItFBMQlBrK8LLNpTaBdHa7T3EMDE,5452
8
8
  linq/_models.py,sha256=vJwz9yya-fpzvMT9srHbvfL4N_0-ZH9WbEWmM2m9NW4,32267
9
9
  linq/_qs.py,sha256=ExvESqZz_a5ZALZG1aR2rkzE35o9Rmk0yPMQtKN0DTo,4924
10
10
  linq/_resource.py,sha256=IvbBkxho8w_Ru0GHWOBUDwiuaI-CQoi8WWQJZCkfhyg,1118
11
11
  linq/_response.py,sha256=Pyf_g81OCXWawxoGZDNp6h1sHMlDgCOsM5vI9fGEBKE,28923
12
12
  linq/_streaming.py,sha256=zmn_k0MQ0d2f4KisSxNc4cqa3mTw2j75iPRyTL5jk9I,10562
13
13
  linq/_types.py,sha256=C3roEXRjDIicI14d6zJMb2C_vkZ8cffHg00DP0UrGYI,7639
14
- linq/_version.py,sha256=PPI3DKy8U4Yx3FKD2_EbIewlbp2cVzzL5DuFlVqziCo,156
14
+ linq/_version.py,sha256=oJAcrdgiuajufLNIFBzD3cqSDzjykSv98MKxu1aX6DA,156
15
15
  linq/pagination.py,sha256=LxWl_sqhWdi6dYzDK1QyEIJd43fvxXMcCwF6Y4ill9I,2464
16
16
  linq/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
- linq/_utils/__init__.py,sha256=DJEWihdzRWrZI23vwocBkgvZCwn9iG7yV0c_9vKaysU,2355
17
+ linq/_utils/__init__.py,sha256=nQq-iFa5YxTaWySaLigatew5rHgTR0M75FNYm4mrO1s,2313
18
18
  linq/_utils/_compat.py,sha256=33246eDcl3pwL6kWsEhVuT4Akrd8gZEW9LPTm465ohk,1231
19
19
  linq/_utils/_datetime_parse.py,sha256=bABTs0Bc6rabdFvnIwXjEhWL15TcRgWZ_6XGTqN8xUk,4204
20
20
  linq/_utils/_json.py,sha256=bl95uuIWwgSfXX-gP1trK_lDAPwJujYfJ05Cxo2SEC4,962
@@ -27,10 +27,10 @@ linq/_utils/_streams.py,sha256=SMC90diFFecpEg_zgDRVbdR3hSEIgVVij4taD-noMLM,289
27
27
  linq/_utils/_sync.py,sha256=HBnZkkBnzxtwOZe0212C4EyoRvxhTVtTrLFDz2_xVCg,1589
28
28
  linq/_utils/_transform.py,sha256=NjCzmnfqYrsAikUHQig6N9QfuTVbKipuP3ur9mcNF-E,15951
29
29
  linq/_utils/_typing.py,sha256=N_5PPuFNsaygbtA_npZd98SVN1LQQvFTKL6bkWPBZGU,4786
30
- linq/_utils/_utils.py,sha256=ugfUaneOK7I8h9b3656flwf5u_kthY0gvNuqvgOLoSU,12252
30
+ linq/_utils/_utils.py,sha256=9dXfN9bD4tR19GWM2SIrua3aBkJ995_5EBdo2NsmMqE,11893
31
31
  linq/lib/.keep,sha256=wuNrz-5SXo3jJaJOJgz4vFHM41YH_g20F5cRQo0vLes,224
32
32
  linq/resources/__init__.py,sha256=5K3fxu-Cf8pMBrTWLLy_9XX2VJXIvs66Wc-DbDgnQeE,4895
33
- linq/resources/attachments.py,sha256=uRn1P0YXK3dp89sdBrwSS8aENx1FMs4qzDBEnvDYOPc,24737
33
+ linq/resources/attachments.py,sha256=uwtQ0Wz-I7-NdRdijoIHnSz5NURPPATBN3ckpEdIhbw,25001
34
34
  linq/resources/capability.py,sha256=-DRB5r-wGMKsTFpYiqK9ktvtwrjBgyrlqF24u8bN4vA,11076
35
35
  linq/resources/contact_card.py,sha256=g2QNjPay7Be2NyYZEMAPVWn4GPLQCc8Kmb5m7F-I6z4,18507
36
36
  linq/resources/messages.py,sha256=kycQ5K8tL3fGQ3jkeJ1eqnuvhTbn6K4A5QyD_8MGTK4,27140
@@ -43,11 +43,11 @@ linq/resources/chats/__init__.py,sha256=D05L41oxeBnO9uNvzHPMcgRD54JJLmwY07T73rjV
43
43
  linq/resources/chats/chats.py,sha256=D22GWoiBgiGXcio2gS_H3I6ISNe9KAobd1nqJcVbMQE,62742
44
44
  linq/resources/chats/messages.py,sha256=7Bp4RFLxHjldQiMruvZAds4EjLgU-PmlDhdXkeVPBAo,16216
45
45
  linq/resources/chats/participants.py,sha256=8Y0Qo84yWIDF9YJ-wm1s5fatPPkKs2cD8dGhPhO82sI,12959
46
- linq/resources/chats/typing.py,sha256=6Z0AVRF3ing2rAC_RyGKksnze-U5z_l5XKagmyvob7g,11808
46
+ linq/resources/chats/typing.py,sha256=gIRJ5t_Q5EjrOZ8u737Uf77g0pxUco15msc9c_Fjr2Y,12802
47
47
  linq/types/__init__.py,sha256=ECn8TN-qe2XWCa5VE_8HVYKPjSUhZmuQi9vmwQT8jOI,6536
48
- linq/types/attachment_create_params.py,sha256=mf00dk0j0xKsHTlE4D0VDZs21iJoFn9pn1jCdcBwaRc,2196
48
+ linq/types/attachment_create_params.py,sha256=PBW1lQ1uC10MUHUexA9D0MrgzZbIjoLWUiebv4ZyfVA,2308
49
49
  linq/types/attachment_create_response.py,sha256=MmZjUj2HMrvt1A6EKM5INSUwc_VNSmjtWdLfRZpFuIA,1230
50
- linq/types/attachment_retrieve_response.py,sha256=V-MdGCPwXb4vfGVEbaOHutz1CIXyLxNfnkjet1C_-Uw,2519
50
+ linq/types/attachment_retrieve_response.py,sha256=YN6nj88bhRbp2P2mFLVMOyfWYGhRRRtm-P_Aed-2r-A,2631
51
51
  linq/types/capability_check_RCS_params.py,sha256=uyKe5xDP1G62NRTBCv4PTh6SZ4o0Qomdjszoei4DXDE,581
52
52
  linq/types/capability_check_i_message_params.py,sha256=bEFb6sJE_IfAyASwmK3CTlLouXJjgvSjTuOYteFUeVI,591
53
53
  linq/types/chat.py,sha256=mAlHuAkTyisq1_ZaO0-nr1qkZo5AdKeRXhApY7Okoyw,1067
@@ -120,7 +120,7 @@ linq/types/chats/participant_add_params.py,sha256=x6Yg1J87R2n095Kg6K0ITz9H1HEVvP
120
120
  linq/types/chats/participant_add_response.py,sha256=JKe4-m-UHb_HgLbw1eZdQx62uWcnPWqX2PMcxAHYe0Q,334
121
121
  linq/types/chats/participant_remove_params.py,sha256=tURRmQZ_Pz2WWxV-ntMpD6CCG65YczQCkRkohn8KxTo,379
122
122
  linq/types/chats/participant_remove_response.py,sha256=qTAxe-rn4T0cengfi-WVGvYLVBXNyT0v5xTRXOYgNx4,340
123
- linq/types/chats/sent_message.py,sha256=qKTqNB_fF1OIQStWGwdFcsBoP6kcUT8R2Cui1WlmZuI,1770
123
+ linq/types/chats/sent_message.py,sha256=H0weXn2x2vk8qA4OsIn471dw-ouSxhd4Ee5t3O8rjMA,1884
124
124
  linq/types/shared/__init__.py,sha256=fUnCGZmx3ThjYwmh42i-kMaWvqSnYWzSnEEeAxkEDTM,561
125
125
  linq/types/shared/chat_handle.py,sha256=eIzMPnOFVs-Ved8RtM_1Ub_97n7KDvc_Z2Dd2Y_8pag,867
126
126
  linq/types/shared/link_part_response.py,sha256=8xOaGAKFnZPGYik2D1X0j8HQZYj84Fh-VI_n06gQIMU,531
@@ -134,7 +134,7 @@ linq/types/shared_params/__init__.py,sha256=FZWRsA1D5DW6IXxenxo9U8-bDW6YiizL-xaw
134
134
  linq/types/shared_params/reaction_type.py,sha256=MP4ekeDAr9lK0wqdvl6FMVa5s0j2w6JhwF8TcF48aSA,317
135
135
  linq/types/shared_params/service_type.py,sha256=AV_VS3d0v059_wou2dts8Jy-NzbGFXa3_VRRpG42N48,259
136
136
  linq/types/shared_params/text_decoration.py,sha256=H-ph17ldBaFRNhajm77DfqjjtQbTNFJj9sepoHHcD80,881
137
- linq_python-0.2.2.dist-info/METADATA,sha256=DsfByi6E7f6NXalo5mQ3lqIxFnBQRTsaPpiTJyC-dc4,17384
138
- linq_python-0.2.2.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
139
- linq_python-0.2.2.dist-info/licenses/LICENSE,sha256=zP4mO5gBwuMBJx2-sVWVdjklSfevmIQT68JUQfvjy40,11341
140
- linq_python-0.2.2.dist-info/RECORD,,
137
+ linq_python-0.2.4.dist-info/METADATA,sha256=cNSLEscU0xkDKJMxn6i1Kf-gVFZp6Y7hSwaqyiNMpkQ,17384
138
+ linq_python-0.2.4.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
139
+ linq_python-0.2.4.dist-info/licenses/LICENSE,sha256=zP4mO5gBwuMBJx2-sVWVdjklSfevmIQT68JUQfvjy40,11341
140
+ linq_python-0.2.4.dist-info/RECORD,,