sg-python-api 0.1.0__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.
@@ -0,0 +1,209 @@
1
+ """Timezone helpers at the official `shotgun_api3.lib.sgtimezone` path."""
2
+
3
+ import datetime
4
+ import time
5
+
6
+ _EPOCH = datetime.datetime(1970, 1, 1, tzinfo=datetime.UTC)
7
+ _NAIVE_EPOCH = _EPOCH.replace(tzinfo=None)
8
+ _ZERO = datetime.timedelta(0)
9
+
10
+
11
+ class LocalTimezone(datetime.tzinfo):
12
+ """The process's dynamic system-local timezone.
13
+
14
+ On platforms without pre-epoch local-time support, pre-1970 values use the
15
+ process's standard offset without historical daylight-saving accuracy.
16
+ """
17
+
18
+ def _isdst(self, dt: datetime.datetime | None) -> bool:
19
+ """Return whether a local wall time selects daylight-saving time."""
20
+ if dt is None or not time.daylight:
21
+ return False
22
+
23
+ wall_time = (dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second)
24
+ time_tuple = (*wall_time, dt.weekday(), 0)
25
+ candidates: list[tuple[float, bool]] = []
26
+ outcomes: list[tuple[float, bool]] = []
27
+ for is_dst in (0, 1):
28
+ try:
29
+ stamp = time.mktime((*time_tuple, is_dst))
30
+ local = time.localtime(stamp)
31
+ except (OSError, OverflowError, ValueError):
32
+ if _is_pre_epoch(dt):
33
+ return False
34
+ raise
35
+ outcome = (stamp, local.tm_isdst > 0)
36
+ outcomes.append(outcome)
37
+ if local[:6] == wall_time and outcome[1] is bool(is_dst):
38
+ candidates.append(outcome)
39
+
40
+ if len(candidates) > 1 and candidates[0][0] != candidates[1][0]:
41
+ candidates.sort(key=lambda candidate: candidate[0])
42
+ return candidates[dt.fold][1]
43
+
44
+ if not candidates and outcomes[0][0] != outcomes[1][0]:
45
+ outcomes.sort(key=lambda outcome: outcome[0])
46
+ return outcomes[dt.fold][1]
47
+
48
+ try:
49
+ inferred_stamp = time.mktime((*time_tuple, -1))
50
+ return time.localtime(inferred_stamp).tm_isdst > 0
51
+ except (OSError, OverflowError, ValueError):
52
+ if _is_pre_epoch(dt):
53
+ return False
54
+ raise
55
+
56
+ @staticmethod
57
+ def _offsets() -> tuple[datetime.timedelta, datetime.timedelta]:
58
+ """Return the process's current standard and daylight offsets."""
59
+ standard = datetime.timedelta(seconds=-time.timezone)
60
+ daylight = datetime.timedelta(seconds=-time.altzone) if time.daylight else standard
61
+ return standard, daylight
62
+
63
+ def dst(self, dt: datetime.datetime | None) -> datetime.timedelta:
64
+ """Return the daylight-saving adjustment for a local wall time.
65
+
66
+ Args:
67
+ dt: Local wall time, with `fold` selecting an ambiguous occurrence.
68
+ `None` selects standard time.
69
+
70
+ Returns:
71
+ The daylight-saving adjustment, or zero when DST is not active.
72
+ """
73
+ standard, daylight = self._offsets()
74
+ return daylight - standard if self._isdst(dt) else _ZERO
75
+
76
+ def fromutc(self, dt: datetime.datetime) -> datetime.datetime:
77
+ """Convert UTC fields to the corresponding local wall time.
78
+
79
+ Args:
80
+ dt: Datetime whose fields express UTC and whose `tzinfo` is this
81
+ instance.
82
+
83
+ Returns:
84
+ The equivalent local datetime, with `fold` marking a repeated wall
85
+ time.
86
+
87
+ Raises:
88
+ ValueError: If `dt.tzinfo` is not this instance.
89
+ """
90
+ if dt.tzinfo is not self:
91
+ raise ValueError('fromutc() requires dt.tzinfo to be self')
92
+
93
+ stamp = (dt.replace(tzinfo=datetime.UTC) - _EPOCH).total_seconds()
94
+ try:
95
+ local = time.localtime(stamp)
96
+ except (OSError, OverflowError, ValueError):
97
+ if stamp >= 0:
98
+ raise
99
+ standard, _daylight = self._offsets()
100
+ return (dt.replace(tzinfo=None) + standard).replace(tzinfo=self, fold=0)
101
+
102
+ standard, daylight = self._offsets()
103
+ dst_difference = (daylight - standard).total_seconds()
104
+ try:
105
+ previous = time.localtime(stamp - dst_difference)
106
+ except (OSError, OverflowError, ValueError):
107
+ if stamp >= 0:
108
+ raise
109
+ standard, _daylight = self._offsets()
110
+ return (dt.replace(tzinfo=None) + standard).replace(tzinfo=self, fold=0)
111
+ fold = bool(dst_difference) and local[:6] == previous[:6]
112
+ return datetime.datetime(
113
+ local.tm_year,
114
+ local.tm_mon,
115
+ local.tm_mday,
116
+ local.tm_hour,
117
+ local.tm_min,
118
+ local.tm_sec,
119
+ microsecond=dt.microsecond,
120
+ tzinfo=self,
121
+ fold=int(fold),
122
+ )
123
+
124
+ def tzname(self, dt: datetime.datetime | None) -> str:
125
+ """Return the current local timezone name for a wall time.
126
+
127
+ Args:
128
+ dt: Local wall time, with `fold` selecting an ambiguous occurrence.
129
+ `None` selects standard time.
130
+
131
+ Returns:
132
+ The platform's standard or daylight timezone name.
133
+ """
134
+ return time.tzname[int(self._isdst(dt))]
135
+
136
+ def utcoffset(self, dt: datetime.datetime | None) -> datetime.timedelta:
137
+ """Return the current local offset from UTC for a wall time.
138
+
139
+ Args:
140
+ dt: Local wall time, with `fold` selecting an ambiguous occurrence.
141
+ `None` selects standard time.
142
+
143
+ Returns:
144
+ The current standard or daylight offset from UTC.
145
+ """
146
+ standard, daylight = self._offsets()
147
+ return daylight if self._isdst(dt) else standard
148
+
149
+
150
+ class UTC(datetime.tzinfo):
151
+ """UTC with a constant zero offset."""
152
+
153
+ def dst(self, dt: datetime.datetime | None) -> datetime.timedelta:
154
+ """Return the zero daylight-saving adjustment.
155
+
156
+ Args:
157
+ dt: Ignored because UTC does not observe daylight-saving time.
158
+
159
+ Returns:
160
+ A zero-length timedelta.
161
+ """
162
+ del dt
163
+ return _ZERO
164
+
165
+ def tzname(self, dt: datetime.datetime | None) -> str:
166
+ """Return the UTC timezone name.
167
+
168
+ Args:
169
+ dt: Ignored because the UTC name is constant.
170
+
171
+ Returns:
172
+ The string `UTC`.
173
+ """
174
+ del dt
175
+ return 'UTC'
176
+
177
+ def utcoffset(self, dt: datetime.datetime | None) -> datetime.timedelta:
178
+ """Return UTC's zero offset.
179
+
180
+ Args:
181
+ dt: Ignored because the UTC offset is constant.
182
+
183
+ Returns:
184
+ A zero-length timedelta.
185
+ """
186
+ del dt
187
+ return _ZERO
188
+
189
+
190
+ class SgTimezone:
191
+ """Container exposing UTC and system-local timezone instances.
192
+
193
+ Attributes:
194
+ utc: UTC timezone instance.
195
+ local: System-local timezone instance.
196
+ """
197
+
198
+ LocalTimezone = LocalTimezone
199
+ UTC = UTC
200
+
201
+ def __init__(self) -> None:
202
+ """Initialize UTC and local timezone instances."""
203
+ self.utc = UTC()
204
+ self.local = LocalTimezone()
205
+
206
+
207
+ def _is_pre_epoch(value: datetime.datetime) -> bool:
208
+ """Return whether a datetime precedes the Unix epoch."""
209
+ return value.replace(tzinfo=None) < _NAIVE_EPOCH
shotgun_api3/py.typed ADDED
File without changes
@@ -0,0 +1,58 @@
1
+ """Expose the synchronous `shotgun_api3.shotgun` compatibility surface.
2
+
3
+ Import this module for the synchronous client and public compatibility names.
4
+ Import initializes the `shotgun_api3` logger and reads
5
+ `SHOTGUN_API_DISABLE_ENTITY_OPTIMIZATION`; the environment value `1` disables
6
+ entity-link optimization.
7
+
8
+ Assigning the module's `SHOTGUN_API_DISABLE_ENTITY_OPTIMIZATION` flag later
9
+ changes the setting used by subsequent request construction.
10
+ """
11
+
12
+ import logging
13
+ import os
14
+
15
+ from ._capabilities import ClientCapabilities, ServerCapabilities
16
+ from ._client import Shotgun, __version__
17
+ from ._errors import (
18
+ AuthenticationFault,
19
+ Error,
20
+ Fault,
21
+ MissingTwoFactorAuthenticationFault,
22
+ ProtocolError,
23
+ ResponseError,
24
+ ShotgunError,
25
+ ShotgunFileDownloadError,
26
+ ShotgunThumbnailNotReady,
27
+ UserCredentialsNotAllowedForOxygenAuthenticationFault,
28
+ UserCredentialsNotAllowedForSSOAuthenticationFault,
29
+ )
30
+ from ._sg_timezone import SG_TIMEZONE
31
+ from .lib.sgtimezone import SgTimezone
32
+
33
+ LOG = logging.getLogger('shotgun_api3')
34
+ SHOTGUN_API_DISABLE_ENTITY_OPTIMIZATION = (
35
+ os.environ.get('SHOTGUN_API_DISABLE_ENTITY_OPTIMIZATION', '0') == '1'
36
+ )
37
+
38
+ __all__ = [
39
+ 'LOG',
40
+ 'SG_TIMEZONE',
41
+ 'SHOTGUN_API_DISABLE_ENTITY_OPTIMIZATION',
42
+ 'AuthenticationFault',
43
+ 'ClientCapabilities',
44
+ 'Error',
45
+ 'Fault',
46
+ 'MissingTwoFactorAuthenticationFault',
47
+ 'ProtocolError',
48
+ 'ResponseError',
49
+ 'ServerCapabilities',
50
+ 'SgTimezone',
51
+ 'Shotgun',
52
+ 'ShotgunError',
53
+ 'ShotgunFileDownloadError',
54
+ 'ShotgunThumbnailNotReady',
55
+ 'UserCredentialsNotAllowedForOxygenAuthenticationFault',
56
+ 'UserCredentialsNotAllowedForSSOAuthenticationFault',
57
+ '__version__',
58
+ ]