itd-sdk 2.4.2__tar.gz → 2.4.3__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.
- {itd_sdk-2.4.2 → itd_sdk-2.4.3}/PKG-INFO +1 -1
- {itd_sdk-2.4.2 → itd_sdk-2.4.3}/itd/base.py +11 -9
- {itd_sdk-2.4.2 → itd_sdk-2.4.3}/itd/client.py +2 -2
- {itd_sdk-2.4.2 → itd_sdk-2.4.3}/itd/comment.py +2 -2
- {itd_sdk-2.4.2 → itd_sdk-2.4.3}/itd/notification.py +1 -1
- {itd_sdk-2.4.2 → itd_sdk-2.4.3}/itd/post.py +7 -6
- {itd_sdk-2.4.2 → itd_sdk-2.4.3}/itd/user.py +3 -14
- {itd_sdk-2.4.2 → itd_sdk-2.4.3}/pyproject.toml +58 -58
- {itd_sdk-2.4.2 → itd_sdk-2.4.3}/README.md +0 -0
- {itd_sdk-2.4.2 → itd_sdk-2.4.3}/itd/__init__.py +0 -0
- {itd_sdk-2.4.2 → itd_sdk-2.4.3}/itd/_default.py +0 -0
- {itd_sdk-2.4.2 → itd_sdk-2.4.3}/itd/api/__init__.py +0 -0
- {itd_sdk-2.4.2 → itd_sdk-2.4.3}/itd/api/auth.py +0 -0
- {itd_sdk-2.4.2 → itd_sdk-2.4.3}/itd/api/comments.py +0 -0
- {itd_sdk-2.4.2 → itd_sdk-2.4.3}/itd/api/dwell.py +0 -0
- {itd_sdk-2.4.2 → itd_sdk-2.4.3}/itd/api/etc.py +0 -0
- {itd_sdk-2.4.2 → itd_sdk-2.4.3}/itd/api/files.py +0 -0
- {itd_sdk-2.4.2 → itd_sdk-2.4.3}/itd/api/hashtags.py +0 -0
- {itd_sdk-2.4.2 → itd_sdk-2.4.3}/itd/api/notifications.py +0 -0
- {itd_sdk-2.4.2 → itd_sdk-2.4.3}/itd/api/pins.py +0 -0
- {itd_sdk-2.4.2 → itd_sdk-2.4.3}/itd/api/platform.py +0 -0
- {itd_sdk-2.4.2 → itd_sdk-2.4.3}/itd/api/polls.py +0 -0
- {itd_sdk-2.4.2 → itd_sdk-2.4.3}/itd/api/posts.py +0 -0
- {itd_sdk-2.4.2 → itd_sdk-2.4.3}/itd/api/reports.py +0 -0
- {itd_sdk-2.4.2 → itd_sdk-2.4.3}/itd/api/search.py +0 -0
- {itd_sdk-2.4.2 → itd_sdk-2.4.3}/itd/api/sessions.py +0 -0
- {itd_sdk-2.4.2 → itd_sdk-2.4.3}/itd/api/subscription.py +0 -0
- {itd_sdk-2.4.2 → itd_sdk-2.4.3}/itd/api/users.py +0 -0
- {itd_sdk-2.4.2 → itd_sdk-2.4.3}/itd/api/verification.py +0 -0
- {itd_sdk-2.4.2 → itd_sdk-2.4.3}/itd/clan.py +0 -0
- {itd_sdk-2.4.2 → itd_sdk-2.4.3}/itd/enums.py +0 -0
- {itd_sdk-2.4.2 → itd_sdk-2.4.3}/itd/exceptions.py +0 -0
- {itd_sdk-2.4.2 → itd_sdk-2.4.3}/itd/file.py +0 -0
- {itd_sdk-2.4.2 → itd_sdk-2.4.3}/itd/hashtag.py +0 -0
- {itd_sdk-2.4.2 → itd_sdk-2.4.3}/itd/logger.py +0 -0
- {itd_sdk-2.4.2 → itd_sdk-2.4.3}/itd/pin.py +0 -0
- {itd_sdk-2.4.2 → itd_sdk-2.4.3}/itd/poll.py +0 -0
- {itd_sdk-2.4.2 → itd_sdk-2.4.3}/itd/report.py +0 -0
- {itd_sdk-2.4.2 → itd_sdk-2.4.3}/itd/request.py +0 -0
- {itd_sdk-2.4.2 → itd_sdk-2.4.3}/itd/session.py +0 -0
- {itd_sdk-2.4.2 → itd_sdk-2.4.3}/itd/span.py +0 -0
- {itd_sdk-2.4.2 → itd_sdk-2.4.3}/itd/utils.py +0 -0
- {itd_sdk-2.4.2 → itd_sdk-2.4.3}/itd/version.py +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
-
from typing import Any, Callable, TYPE_CHECKING, Iterator, TypeVar, overload
|
|
2
|
+
from typing import Any, Callable, TYPE_CHECKING, Iterator, TypeVar, overload
|
|
3
3
|
from functools import wraps
|
|
4
4
|
from time import sleep
|
|
5
5
|
from datetime import datetime, timedelta
|
|
@@ -53,11 +53,12 @@ class ITDBaseModel:
|
|
|
53
53
|
|
|
54
54
|
def __init__(self, client: Client | None = None) -> None:
|
|
55
55
|
self._client = client or get_default_client()
|
|
56
|
-
self.
|
|
56
|
+
self._loaded_attrs: set[str] = set()
|
|
57
57
|
|
|
58
58
|
def __setattr__(self, name: str, value: Any) -> None:
|
|
59
59
|
if isinstance(value, ITDBaseModel) and (client := _getattr(self, '_client')): # ai
|
|
60
60
|
value._client = client
|
|
61
|
+
|
|
61
62
|
object.__setattr__(self, name, value)
|
|
62
63
|
|
|
63
64
|
def _post_refresh(self): ...
|
|
@@ -80,7 +81,7 @@ class ITDBaseModel:
|
|
|
80
81
|
def _fill_from_data(self, data: dict):
|
|
81
82
|
assert self._validator, 'Unable to use fill_from_data without a validator'
|
|
82
83
|
validated = self._validator().model_validate(data) # pyright: ignore[reportCallIssue]
|
|
83
|
-
self.
|
|
84
|
+
# self._loaded_attrs = validated.model_fields_set # значения автоматически добавляются через __setattr__
|
|
84
85
|
for name, value in validated.__dict__.items():
|
|
85
86
|
setattr(self, name, value)
|
|
86
87
|
|
|
@@ -102,18 +103,20 @@ class ITDBaseModel:
|
|
|
102
103
|
value = _getattr(self, name)
|
|
103
104
|
if (
|
|
104
105
|
name.startswith('_') or # приватный аттрибут
|
|
105
|
-
name in ('client', 'model_fields_set', 'load_status') or
|
|
106
|
+
name in ('client', 'model_fields_set', 'load_status') or # спец. имя
|
|
106
107
|
not _getattr(self, '_refreshable') or # не рефрешабельная модель
|
|
107
108
|
not _getattr(self, 'client').config.auto_load or # отключено в конфиге
|
|
108
109
|
callable(value) or # функция
|
|
109
|
-
self.load_status == LoadStatus.LOADING or
|
|
110
|
-
(not _getattr(self, 'client').config.load_comments_from_post and _is_comments_on_post(value, self)) or
|
|
110
|
+
self.load_status == LoadStatus.LOADING or # сейчас загружается (уже вроде как не надо, но пусть будет на всяк)
|
|
111
|
+
(not _getattr(self, 'client').config.load_comments_from_post and _is_comments_on_post(value, self)) or # коменты
|
|
112
|
+
name in self._loaded_attrs or name in self.__dict__ or # было загружено или добавлено через setattr
|
|
111
113
|
(isinstance(value, ITDBaseModel) and not value._load_with_parent)
|
|
112
114
|
):
|
|
113
115
|
# l.debug('return %s as is', name)
|
|
114
116
|
return object.__getattribute__(self, name)
|
|
115
117
|
|
|
116
|
-
if isinstance(value, FieldInfo) or
|
|
118
|
+
if isinstance(value, FieldInfo) or self.load_status != LoadStatus.FULL:
|
|
119
|
+
l.debug('attrs %s', list(self._loaded_attrs))
|
|
117
120
|
l.info('refresh %s field=%s load_status=%s', self.__class__.__name__, name, self.load_status.value)
|
|
118
121
|
self.refresh()
|
|
119
122
|
|
|
@@ -243,7 +246,6 @@ class ITDList[T](ITDBaseModel, list[T]):
|
|
|
243
246
|
def __getitem__(self, index: slice) -> list[T]: ...
|
|
244
247
|
|
|
245
248
|
def __getitem__(self, index: int | slice) -> T | list[T]: # pyright: ignore[reportIncompatibleMethodOverride]
|
|
246
|
-
|
|
247
249
|
if isinstance(index, slice):
|
|
248
250
|
value: int | None = index.stop
|
|
249
251
|
else:
|
|
@@ -404,7 +406,7 @@ def rate_limit(delay_min: float | None = None, delay_mid: float | None = None, d
|
|
|
404
406
|
l.error('too large rate limit')
|
|
405
407
|
raise
|
|
406
408
|
|
|
407
|
-
retry_after = getattr(e, 'retry_after', client.config.retry_delay)
|
|
409
|
+
retry_after = getattr(e, 'retry_after', client.config.retry_delay) or 10
|
|
408
410
|
l.warning('%s on %s: wait %ss', e.__class__.__name__, func.__name__, retry_after)
|
|
409
411
|
sleep(retry_after)
|
|
410
412
|
|
|
@@ -65,7 +65,7 @@ class Config:
|
|
|
65
65
|
|
|
66
66
|
retry_enabled: bool = True
|
|
67
67
|
retry_delay: float = 10 # delay before next attempt (after rate limit error) if retry_after is not provided in request
|
|
68
|
-
retry_max_retries: int | None =
|
|
68
|
+
retry_max_retries: int | None = 10 # none for no limit
|
|
69
69
|
retry_exceptions: tuple[type[Exception]] | list[type[Exception]] | None = None
|
|
70
70
|
retry_max_retry_after: int = 500
|
|
71
71
|
|
|
@@ -287,7 +287,7 @@ class Client:
|
|
|
287
287
|
tuple[list[User], list[Hashtag]]: Результат поиска
|
|
288
288
|
"""
|
|
289
289
|
res = search(self, query, users_limit, hashtags_limit).json()['data']
|
|
290
|
-
return [User.
|
|
290
|
+
return [User.from_dict(user, client=self) for user in res['users']], [Hashtag._from_dict(hashtag, self) for hashtag in res['hashtags']]
|
|
291
291
|
|
|
292
292
|
def search_users(self, query: str, limit: int = 20) -> list[User]:
|
|
293
293
|
"""Поиск пользователей
|
|
@@ -152,12 +152,12 @@ class _CommentValidate(BaseModel, Comment):
|
|
|
152
152
|
@classmethod
|
|
153
153
|
def validate_reply_to(cls, reply_to: dict | None):
|
|
154
154
|
if reply_to is not None:
|
|
155
|
-
return User.
|
|
155
|
+
return User.from_dict(reply_to)
|
|
156
156
|
|
|
157
157
|
@field_validator('author', mode='plain')
|
|
158
158
|
@classmethod
|
|
159
159
|
def validate_author(cls, author: dict):
|
|
160
|
-
return User.
|
|
160
|
+
return User.from_dict(author)
|
|
161
161
|
|
|
162
162
|
|
|
163
163
|
|
|
@@ -289,6 +289,7 @@ class Post(ITDBaseModel):
|
|
|
289
289
|
Returns:
|
|
290
290
|
Post: Пост
|
|
291
291
|
"""
|
|
292
|
+
# TODO rewrite with from_dict
|
|
292
293
|
instance = cls.__new__(cls)
|
|
293
294
|
super(Post, instance).__init__(client)
|
|
294
295
|
|
|
@@ -311,7 +312,7 @@ class Post(ITDBaseModel):
|
|
|
311
312
|
).json()
|
|
312
313
|
|
|
313
314
|
validated = _PostValidate.model_validate(post)
|
|
314
|
-
instance.
|
|
315
|
+
instance._loaded_attrs = validated.model_fields_set
|
|
315
316
|
for name, value in validated.__dict__.items():
|
|
316
317
|
setattr(instance, name, value)
|
|
317
318
|
|
|
@@ -405,7 +406,7 @@ class Post(ITDBaseModel):
|
|
|
405
406
|
Post: Пост
|
|
406
407
|
"""
|
|
407
408
|
post = repost(client or self.client, self.id, content).json()
|
|
408
|
-
if 'reposts_count' in self.
|
|
409
|
+
if 'reposts_count' in self._loaded_attrs:
|
|
409
410
|
self.reposts_count += 1
|
|
410
411
|
if (client or self.client) == self.client:
|
|
411
412
|
self.is_reposted = True
|
|
@@ -429,7 +430,7 @@ class Post(ITDBaseModel):
|
|
|
429
430
|
view_post(c, self.id)
|
|
430
431
|
if c == self.client:
|
|
431
432
|
self.is_viewed = True
|
|
432
|
-
if c.config.post_view_increment and 'views_count' in self.
|
|
433
|
+
if c.config.post_view_increment and 'views_count' in self._loaded_attrs:
|
|
433
434
|
self.views_count += 1
|
|
434
435
|
|
|
435
436
|
def pin(self, client: Client | None = None) -> None:
|
|
@@ -505,7 +506,7 @@ class Post(ITDBaseModel):
|
|
|
505
506
|
Comment: Комментарий
|
|
506
507
|
"""
|
|
507
508
|
comment = self.comments.new(content, attachments, client or self.client)
|
|
508
|
-
if 'comments_count' in self.
|
|
509
|
+
if 'comments_count' in self._loaded_attrs:
|
|
509
510
|
self.comments_count += 1
|
|
510
511
|
return comment
|
|
511
512
|
|
|
@@ -588,13 +589,13 @@ class _PostValidate(BaseModel, Post): # BaseModel MUST be first or you ll have s
|
|
|
588
589
|
return None
|
|
589
590
|
if isinstance(author, _UserBase):
|
|
590
591
|
return author
|
|
591
|
-
return User.
|
|
592
|
+
return User.from_dict(author)
|
|
592
593
|
|
|
593
594
|
@field_validator('wall_recipient', mode='plain')
|
|
594
595
|
@classmethod
|
|
595
596
|
def validate_wall_recipient(cls, wall_recipient: dict | None):
|
|
596
597
|
if wall_recipient is not None:
|
|
597
|
-
return User.
|
|
598
|
+
return User.from_dict(wall_recipient)
|
|
598
599
|
|
|
599
600
|
|
|
600
601
|
|
|
@@ -250,17 +250,6 @@ class User(_UserBase):
|
|
|
250
250
|
|
|
251
251
|
created_at: datetime | None = Field(None, alias='createdAt') # none if blocked
|
|
252
252
|
|
|
253
|
-
@classmethod
|
|
254
|
-
def _from_dict(cls, data: dict, client: Client | None = None):
|
|
255
|
-
instance = cls(data['username'], client)
|
|
256
|
-
validated = _UserValidate.model_validate(data)
|
|
257
|
-
instance._fields_from_data = validated.model_fields_set
|
|
258
|
-
for name, value in validated.__dict__.items():
|
|
259
|
-
setattr(instance, name, value)
|
|
260
|
-
|
|
261
|
-
instance.load_status = LoadStatus.PARTIALLY
|
|
262
|
-
return instance
|
|
263
|
-
|
|
264
253
|
@classmethod
|
|
265
254
|
def by_id(cls, id: UUID | str) -> 'User':
|
|
266
255
|
return cls(to_uuid(id))
|
|
@@ -382,13 +371,13 @@ class User(_UserBase):
|
|
|
382
371
|
@property
|
|
383
372
|
def following(self) -> list[User]:
|
|
384
373
|
if not self._following:
|
|
385
|
-
self._following = [User.
|
|
374
|
+
self._following = [User.from_dict(user, client=self.client) for user in get_following(self.client, self._identifier).json()['data']['users']]
|
|
386
375
|
return self._following
|
|
387
376
|
|
|
388
377
|
@property
|
|
389
378
|
def followers(self) -> list[User]:
|
|
390
379
|
if not self._followers:
|
|
391
|
-
self._followers = [User.
|
|
380
|
+
self._followers = [User.from_dict(user, client=self.client) for user in get_followers(self.client, self._identifier).json()['data']['users']]
|
|
392
381
|
return self._followers
|
|
393
382
|
|
|
394
383
|
|
|
@@ -582,7 +571,7 @@ class Followers(ITDList[User]):
|
|
|
582
571
|
return data['pagination']['page'] + 1
|
|
583
572
|
|
|
584
573
|
def _extend(self, objects: list, client: Client):
|
|
585
|
-
self.extend([User.
|
|
574
|
+
self.extend([User.from_dict(user, client=client) for user in objects])
|
|
586
575
|
|
|
587
576
|
def __setattr__(self, name: str, value) -> None:
|
|
588
577
|
if name == '_client':
|
|
@@ -1,58 +1,58 @@
|
|
|
1
|
-
[build-system]
|
|
2
|
-
requires = ["uv_build~=0.11"]
|
|
3
|
-
build-backend = "uv_build"
|
|
4
|
-
|
|
5
|
-
[tool.uv.build-backend]
|
|
6
|
-
module-name = 'itd'
|
|
7
|
-
module-root = ''
|
|
8
|
-
|
|
9
|
-
[tool.commitizen]
|
|
10
|
-
name = "cz_conventional_commits"
|
|
11
|
-
tag_format = "v$version"
|
|
12
|
-
version_scheme = "semver2"
|
|
13
|
-
version_provider = "uv"
|
|
14
|
-
|
|
15
|
-
[project]
|
|
16
|
-
name = "itd-sdk"
|
|
17
|
-
version = "2.4.
|
|
18
|
-
description = "ITD client for python"
|
|
19
|
-
readme = "README.md"
|
|
20
|
-
authors = [
|
|
21
|
-
{ name = "firedotguy", email = "nta16022013@gmail.com" }
|
|
22
|
-
]
|
|
23
|
-
classifiers = [
|
|
24
|
-
'Development Status :: 5 - Production/Stable',
|
|
25
|
-
'Natural Language :: Russian',
|
|
26
|
-
'Programming Language :: Python',
|
|
27
|
-
'Programming Language :: Python :: 3',
|
|
28
|
-
'Framework :: Pydantic :: 2'
|
|
29
|
-
]
|
|
30
|
-
keywords = ['itd', 'itdsocial']
|
|
31
|
-
license = "MIT"
|
|
32
|
-
dependencies = [
|
|
33
|
-
"requests~=2.32.5",
|
|
34
|
-
"pydantic>=2.12.5,<2.14.0",
|
|
35
|
-
"sseclient-py~=1.9.0",
|
|
36
|
-
]
|
|
37
|
-
requires-python = ">=3.9"
|
|
38
|
-
|
|
39
|
-
[project.urls]
|
|
40
|
-
Homepage = 'https://itdsdk.qzz.io'
|
|
41
|
-
Documentation = 'https://itdsdk.qzz.io/docs'
|
|
42
|
-
|
|
43
|
-
[project.optional-dependencies]
|
|
44
|
-
filetype = [
|
|
45
|
-
"filetype>=1.2.0",
|
|
46
|
-
]
|
|
47
|
-
logs = [
|
|
48
|
-
"rich>=15.0.0",
|
|
49
|
-
]
|
|
50
|
-
date = [
|
|
51
|
-
"dateparser>=1.2.2",
|
|
52
|
-
]
|
|
53
|
-
|
|
54
|
-
[dependency-groups]
|
|
55
|
-
dev = [
|
|
56
|
-
"pytest>=8.4.2",
|
|
57
|
-
"python-dotenv>=1.2.1",
|
|
58
|
-
]
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["uv_build~=0.11"]
|
|
3
|
+
build-backend = "uv_build"
|
|
4
|
+
|
|
5
|
+
[tool.uv.build-backend]
|
|
6
|
+
module-name = 'itd'
|
|
7
|
+
module-root = ''
|
|
8
|
+
|
|
9
|
+
[tool.commitizen]
|
|
10
|
+
name = "cz_conventional_commits"
|
|
11
|
+
tag_format = "v$version"
|
|
12
|
+
version_scheme = "semver2"
|
|
13
|
+
version_provider = "uv"
|
|
14
|
+
|
|
15
|
+
[project]
|
|
16
|
+
name = "itd-sdk"
|
|
17
|
+
version = "2.4.3"
|
|
18
|
+
description = "ITD client for python"
|
|
19
|
+
readme = "README.md"
|
|
20
|
+
authors = [
|
|
21
|
+
{ name = "firedotguy", email = "nta16022013@gmail.com" }
|
|
22
|
+
]
|
|
23
|
+
classifiers = [
|
|
24
|
+
'Development Status :: 5 - Production/Stable',
|
|
25
|
+
'Natural Language :: Russian',
|
|
26
|
+
'Programming Language :: Python',
|
|
27
|
+
'Programming Language :: Python :: 3',
|
|
28
|
+
'Framework :: Pydantic :: 2'
|
|
29
|
+
]
|
|
30
|
+
keywords = ['itd', 'itdsocial']
|
|
31
|
+
license = "MIT"
|
|
32
|
+
dependencies = [
|
|
33
|
+
"requests~=2.32.5",
|
|
34
|
+
"pydantic>=2.12.5,<2.14.0",
|
|
35
|
+
"sseclient-py~=1.9.0",
|
|
36
|
+
]
|
|
37
|
+
requires-python = ">=3.9"
|
|
38
|
+
|
|
39
|
+
[project.urls]
|
|
40
|
+
Homepage = 'https://itdsdk.qzz.io'
|
|
41
|
+
Documentation = 'https://itdsdk.qzz.io/docs'
|
|
42
|
+
|
|
43
|
+
[project.optional-dependencies]
|
|
44
|
+
filetype = [
|
|
45
|
+
"filetype>=1.2.0",
|
|
46
|
+
]
|
|
47
|
+
logs = [
|
|
48
|
+
"rich>=15.0.0",
|
|
49
|
+
]
|
|
50
|
+
date = [
|
|
51
|
+
"dateparser>=1.2.2",
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
[dependency-groups]
|
|
55
|
+
dev = [
|
|
56
|
+
"pytest>=8.4.2",
|
|
57
|
+
"python-dotenv>=1.2.1",
|
|
58
|
+
]
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|