omdev 0.0.0.dev213__py3-none-any.whl → 0.0.0.dev215__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -4962,6 +4962,7 @@ inj = InjectionApi()
4962
4962
  TODO:
4963
4963
  - pickle stdlib objs? have to pin to 3.8 pickle protocol, will be cross-version
4964
4964
  - literals
4965
+ - Options.sequence_cls = list, mapping_cls = dict, ... - def with_mutable_containers() -> Options
4965
4966
  """
4966
4967
 
4967
4968
 
@@ -5114,21 +5115,55 @@ class IterableObjMarshaler(ObjMarshaler):
5114
5115
  @dc.dataclass(frozen=True)
5115
5116
  class FieldsObjMarshaler(ObjMarshaler):
5116
5117
  ty: type
5117
- fs: ta.Mapping[str, ObjMarshaler]
5118
+
5119
+ @dc.dataclass(frozen=True)
5120
+ class Field:
5121
+ att: str
5122
+ key: str
5123
+ m: ObjMarshaler
5124
+
5125
+ omit_if_none: bool = False
5126
+
5127
+ fs: ta.Sequence[Field]
5128
+
5118
5129
  non_strict: bool = False
5119
5130
 
5131
+ #
5132
+
5133
+ _fs_by_att: ta.ClassVar[ta.Mapping[str, Field]]
5134
+ _fs_by_key: ta.ClassVar[ta.Mapping[str, Field]]
5135
+
5136
+ def __post_init__(self) -> None:
5137
+ fs_by_att: dict = {}
5138
+ fs_by_key: dict = {}
5139
+ for f in self.fs:
5140
+ check.not_in(check.non_empty_str(f.att), fs_by_att)
5141
+ check.not_in(check.non_empty_str(f.key), fs_by_key)
5142
+ fs_by_att[f.att] = f
5143
+ fs_by_key[f.key] = f
5144
+ self.__dict__['_fs_by_att'] = fs_by_att
5145
+ self.__dict__['_fs_by_key'] = fs_by_key
5146
+
5147
+ #
5148
+
5120
5149
  def marshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
5121
- return {
5122
- k: m.marshal(getattr(o, k), ctx)
5123
- for k, m in self.fs.items()
5124
- }
5150
+ d = {}
5151
+ for f in self.fs:
5152
+ mv = f.m.marshal(getattr(o, f.att), ctx)
5153
+ if mv is None and f.omit_if_none:
5154
+ continue
5155
+ d[f.key] = mv
5156
+ return d
5125
5157
 
5126
5158
  def unmarshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
5127
- return self.ty(**{
5128
- k: self.fs[k].unmarshal(v, ctx)
5129
- for k, v in o.items()
5130
- if not (self.non_strict or ctx.options.non_strict_fields) or k in self.fs
5131
- })
5159
+ kw = {}
5160
+ for k, v in o.items():
5161
+ if (f := self._fs_by_key.get(k)) is None:
5162
+ if not (self.non_strict or ctx.options.non_strict_fields):
5163
+ raise KeyError(k)
5164
+ continue
5165
+ kw[f.att] = f.m.unmarshal(v, ctx)
5166
+ return self.ty(**kw)
5132
5167
 
5133
5168
 
5134
5169
  @dc.dataclass(frozen=True)
@@ -5263,6 +5298,22 @@ def register_single_field_type_obj_marshaler(fld, ty=None):
5263
5298
  ##
5264
5299
 
5265
5300
 
5301
+ class ObjMarshalerFieldMetadata:
5302
+ def __new__(cls, *args, **kwargs): # noqa
5303
+ raise TypeError
5304
+
5305
+
5306
+ class OBJ_MARSHALER_FIELD_KEY(ObjMarshalerFieldMetadata): # noqa
5307
+ pass
5308
+
5309
+
5310
+ class OBJ_MARSHALER_OMIT_IF_NONE(ObjMarshalerFieldMetadata): # noqa
5311
+ pass
5312
+
5313
+
5314
+ ##
5315
+
5316
+
5266
5317
  class ObjMarshalerManager:
5267
5318
  def __init__(
5268
5319
  self,
@@ -5322,14 +5373,30 @@ class ObjMarshalerManager:
5322
5373
  if dc.is_dataclass(ty):
5323
5374
  return FieldsObjMarshaler(
5324
5375
  ty,
5325
- {f.name: rec(f.type) for f in dc.fields(ty)},
5376
+ [
5377
+ FieldsObjMarshaler.Field(
5378
+ att=f.name,
5379
+ key=check.non_empty_str(fk),
5380
+ m=rec(f.type),
5381
+ omit_if_none=check.isinstance(f.metadata.get(OBJ_MARSHALER_OMIT_IF_NONE, False), bool),
5382
+ )
5383
+ for f in dc.fields(ty)
5384
+ if (fk := f.metadata.get(OBJ_MARSHALER_FIELD_KEY, f.name)) is not None
5385
+ ],
5326
5386
  non_strict=non_strict_fields,
5327
5387
  )
5328
5388
 
5329
5389
  if issubclass(ty, tuple) and hasattr(ty, '_fields'):
5330
5390
  return FieldsObjMarshaler(
5331
5391
  ty,
5332
- {p.name: rec(p.annotation) for p in inspect.signature(ty).parameters.values()},
5392
+ [
5393
+ FieldsObjMarshaler.Field(
5394
+ att=p.name,
5395
+ key=p.name,
5396
+ m=rec(p.annotation),
5397
+ )
5398
+ for p in inspect.signature(ty).parameters.values()
5399
+ ],
5333
5400
  non_strict=non_strict_fields,
5334
5401
  )
5335
5402
 
omdev/tools/docker.py CHANGED
@@ -1,6 +1,12 @@
1
1
  """
2
2
  TODO:
3
3
  - https://github.com/zeromake/docker-debug
4
+ - prune
5
+ - docker container prune --force
6
+ - docker system prune --all --force --filter "until=720h"
7
+ - docker container prune --force --filter "until=720h"
8
+ - docker image prune --all --force --filter "until=720h"
9
+ - docker builder prune --all --force --filter "until=720h"
4
10
  """
5
11
  import os
6
12
  import re
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: omdev
3
- Version: 0.0.0.dev213
3
+ Version: 0.0.0.dev215
4
4
  Summary: omdev
5
5
  Author: wrmsr
6
6
  License: BSD-3-Clause
@@ -12,7 +12,7 @@ Classifier: Operating System :: OS Independent
12
12
  Classifier: Operating System :: POSIX
13
13
  Requires-Python: >=3.12
14
14
  License-File: LICENSE
15
- Requires-Dist: omlish==0.0.0.dev213
15
+ Requires-Dist: omlish==0.0.0.dev215
16
16
  Provides-Extra: all
17
17
  Requires-Dist: black~=24.10; extra == "all"
18
18
  Requires-Dist: pycparser~=2.22; extra == "all"
@@ -1,4 +1,4 @@
1
- omdev/.manifests.json,sha256=kIHxCLwrqvBumPKT0GKsgRPhrJSovHf9jDnWzUDZMHs,9092
1
+ omdev/.manifests.json,sha256=VUg0T7DFMxatE-hsLqocMvGoNmELDiZZaudO1tcz-FE,9092
2
2
  omdev/__about__.py,sha256=j3vFclhFvyPICV6FK4aDApFzMCqJWxv9FaWwdwXrSgw,1215
3
3
  omdev/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  omdev/bracepy.py,sha256=I8EdqtDvxzAi3I8TuMEW-RBfwXfqKbwp06CfOdj3L1o,2743
@@ -69,22 +69,24 @@ omdev/cexts/_distutils/compilers/__init__.py,sha256=amL_zrFlba0lHIvpqDne9uhqhLem
69
69
  omdev/cexts/_distutils/compilers/ccompiler.py,sha256=cTs88qrvj0hBVXHfemSDE_du_nEA4_qo3Qst5TpQkVI,43606
70
70
  omdev/cexts/_distutils/compilers/options.py,sha256=H7r5IcLvga5Fs3jjXWIT-6ap3JBduXRKgtpDmSGCZxs,3818
71
71
  omdev/cexts/_distutils/compilers/unixccompiler.py,sha256=o1h8QuyupLntv4F21_XjzAZmCiwwxJuTmOirvBSL-Qw,15419
72
- omdev/ci/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
72
+ omdev/ci/__init__.py,sha256=Y3l4WY4JRi2uLG6kgbGp93fuGfkxkKwZDvhsa0Rwgtk,15
73
73
  omdev/ci/__main__.py,sha256=Jsrv3P7LX2Cg08W7ByZfZ1JQT4lgLDPW1qNAmShFuMk,75
74
- omdev/ci/cache.py,sha256=jGrsnYNHnnOLJGIEoyHJUdpVzCBdh4tmy2d22Xgarnk,4132
75
- omdev/ci/ci.py,sha256=D7NkXNIABthkAeXuI0e3HCIVB4IJOURtWxQJ3t4TCOc,8369
76
- omdev/ci/cli.py,sha256=G3gmqWmqehlzzmd4bfnYWP9dTaxYQP7VcsbT8CNtCr8,5549
77
- omdev/ci/compose.py,sha256=X5j4imFa6QxYDONFRmhXOD8L9a-76zRfu8QJfVPlc9w,4726
78
- omdev/ci/docker.py,sha256=5FNQXlyvbBgOJw6jFyb4don_IYbT6fVgE09wIh7rYEk,3785
79
- omdev/ci/requirements.py,sha256=9K-f3DXZANoOonLjCCtpE6wR8JPJxj33ouo0VKNjZUE,2115
80
- omdev/ci/shell.py,sha256=KTkVZb_piQeB5Z12M2jUS_zGJfLR_Y2W2uYkrQpJpyw,850
81
- omdev/ci/utils.py,sha256=yiLSFOy4nNN9EY1C8rBe8fXdTmjpfBb6n1IgpzVJ00g,1605
74
+ omdev/ci/cache.py,sha256=hHV7ovvOwkSRcIF49ovb1A7s43mUl0IWXi1iIQeRMdA,3643
75
+ omdev/ci/ci.py,sha256=lEP5wgX0rInvkLJtGZQ-NSLO1Fc0CdQjVaUoMmeRCjQ,8110
76
+ omdev/ci/cli.py,sha256=0KBGKxaKzhSbhk3YFDOgzrb_OVOY22a4qpIaTu5zBeA,6243
77
+ omdev/ci/compose.py,sha256=vHLuXO5e2paafBC0Kf-OUGoamtIJmQ19r2U3_oikk_g,4541
78
+ omdev/ci/consts.py,sha256=1puYfksvGOaVWEnbARM_sdMqs8oTn_VvsevsOtLsFno,21
79
+ omdev/ci/docker.py,sha256=d3wQyL-N4mmzkIVbSgNx2Xd-K6RniFQN8nR-THKuDZs,3770
80
+ omdev/ci/requirements.py,sha256=iKAINTDyVQaqESGlfE9OXkKiW_CZ9kP70PEc121Ix-Q,2100
81
+ omdev/ci/shell.py,sha256=cBPLMKiAJuNpPGj3ou6hpl88Xw7r99xpL91KJBQ0rqw,835
82
+ omdev/ci/utils.py,sha256=mnf6yZmholvj0s_7JGbaO0Q1EWCn_saSv6BKMQQUCoQ,1450
82
83
  omdev/ci/github/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
83
- omdev/ci/github/bootstrap.py,sha256=KZxcg97DsbuE0UuL1G1qTqQEPjr3xiyJvALGnIyMz4s,249
84
- omdev/ci/github/cache.py,sha256=mItUXedRzffBHmiLCg6NdP07vDBDNf37pKgdtx6rFF0,9129
85
- omdev/ci/github/cacheapi.py,sha256=h7iJDcMicXYPf_4gqwUZu3DnaEp83ndMdVmGZXZUGas,5213
86
- omdev/ci/github/cli.py,sha256=ttBYHRIEXQZ6xi12aGZOgJJRfR22twyi3vL9R6eHvRA,975
87
- omdev/ci/github/curl.py,sha256=0UnLuOD1NR3b--Gr_T7klSub4zOFkQZcnXn7htd2j-M,5398
84
+ omdev/ci/github/api.py,sha256=Vqza7Hm1OCSfZYgdXF4exkjneqNjFcdO1pl8qmODskU,5198
85
+ omdev/ci/github/bootstrap.py,sha256=9OuftAz7CUd7uf2Or3sJFVozQQiwu0RGAlTOQNpLQIY,430
86
+ omdev/ci/github/cache.py,sha256=cXbPPMIWK-qjGyuCScEJGe-l4QKLwGBkW0GxOhGO_8E,1801
87
+ omdev/ci/github/cli.py,sha256=6mG0CllwrOoC7MDzKfKDqBHAjfF0gEI6aT5UAGMmuss,1114
88
+ omdev/ci/github/client.py,sha256=cSewCoHC4cDM4BMQObcrf5g2wwD6rTlil_h2repAkf8,14341
89
+ omdev/ci/github/env.py,sha256=FQFjP_m7JWM7es9I51U-6UgJTwAt_UCVHFIYKTd9NKM,394
88
90
  omdev/cli/__init__.py,sha256=V_l6VP1SZMlJbO-8CJwSuO9TThOy2S_oaPepNYgIrbE,37
89
91
  omdev/cli/__main__.py,sha256=mOJpgc07o0r5luQ1DlX4tk2PqZkgmbwPbdzJ3KmtjgQ,138
90
92
  omdev/cli/_pathhack.py,sha256=kxqb2kHap68Lkh8b211rDbcgj06hidBiAKA3f9posyc,2119
@@ -172,12 +174,12 @@ omdev/pyproject/resources/docker-dev.sh,sha256=DHkz5D18jok_oDolfg2mqrvGRWFoCe9GQ
172
174
  omdev/pyproject/resources/python.sh,sha256=jvrwddYw2KNttpuImLbdCdJK0HsUNMrHtTnmLvhxQxg,757
173
175
  omdev/scripts/__init__.py,sha256=MKCvUAEQwsIvwLixwtPlpBqmkMXLCnjjXyAXvVpDwVk,91
174
176
  omdev/scripts/bumpversion.py,sha256=Kn7fo73Hs8uJh3Hi3EIyLOlzLPWAC6dwuD_lZ3cIzuY,1064
175
- omdev/scripts/ci.py,sha256=g3jY4hHJaYu9cWnJJgk56YYGw_9USgPRY_45IQHFx-4,102884
177
+ omdev/scripts/ci.py,sha256=yDx6VsztaY1VqYWF1i9Ywt2YTIseSc5wYtdvtmh5qyI,108870
176
178
  omdev/scripts/execrss.py,sha256=mR0G0wERBYtQmVIn63lCIIFb5zkCM6X_XOENDFYDBKc,651
177
179
  omdev/scripts/exectime.py,sha256=sFb376GflU6s9gNX-2-we8hgH6w5MuQNS9g6i4SqJIo,610
178
180
  omdev/scripts/importtrace.py,sha256=oa7CtcWJVMNDbyIEiRHej6ICfABfErMeo4_haIqe18Q,14041
179
181
  omdev/scripts/interp.py,sha256=7NrLbOkiDjBldnzpf-EpL8UTmU6U3lakvHqhSlY3X_U,141851
180
- omdev/scripts/pyproject.py,sha256=GaA3zZ6kRGbGl9tdpJCsJMGEEj3ltTGt8kprWieMQ98,243677
182
+ omdev/scripts/pyproject.py,sha256=HDors8tvpSgCUzESwQ2E-Gx5jLisaYoOApOTCgmQ5P0,245595
181
183
  omdev/scripts/slowcat.py,sha256=lssv4yrgJHiWfOiHkUut2p8E8Tq32zB-ujXESQxFFHY,2728
182
184
  omdev/scripts/tmpexec.py,sha256=WTYcf56Tj2qjYV14AWmV8SfT0u6Y8eIU6cKgQRvEK3c,1442
183
185
  omdev/tokens/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -187,7 +189,7 @@ omdev/tokens/utils.py,sha256=DJA2jsDzrtQxnO0bdkUk1-GmenX4ECugdhl1xOBe7QI,1448
187
189
  omdev/tools/__init__.py,sha256=iVJAOQ0viGTQOm0DLX4uZLro-9jOioYJGLg9s0kDx1A,78
188
190
  omdev/tools/cloc.py,sha256=jYlMHBae9oGKN4VKeBGuqjiQNcM2be7KIoTF0oNwx_I,5205
189
191
  omdev/tools/doc.py,sha256=wvgGhv6aFaV-Zl-Qivejx37i-lKQ207rZ-4K2fPf-Ss,2547
190
- omdev/tools/docker.py,sha256=KVFckA8eAdiapFUr8xkfMw9Uv3Qy4oNq0e70Lqt1F7I,7352
192
+ omdev/tools/docker.py,sha256=mRd5ziNOnJSJ__9MlldASIF6bBD7m48fDKCD1VEUNUw,7633
191
193
  omdev/tools/git.py,sha256=fiQc4w2w63PELLLyMXdwpmgpzFzs7UNn35vewWckScM,7514
192
194
  omdev/tools/importscan.py,sha256=nhJIhtjDY6eFVlReP7fegvv6L5ZjN-Z2VeyhsBonev4,4639
193
195
  omdev/tools/linehisto.py,sha256=0ZNm34EuiZBE9Q2YC6KNLNNydNT8QPSOwvYzXiU9S2Q,8881
@@ -209,9 +211,9 @@ omdev/tools/json/rendering.py,sha256=tMcjOW5edfozcMSTxxvF7WVTsbYLoe9bCKFh50qyaGw
209
211
  omdev/tools/pawk/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
210
212
  omdev/tools/pawk/__main__.py,sha256=VCqeRVnqT1RPEoIrqHFSu4PXVMg4YEgF4qCQm90-eRI,66
211
213
  omdev/tools/pawk/pawk.py,sha256=zsEkfQX0jF5bn712uqPAyBSdJt2dno1LH2oeSMNfXQI,11424
212
- omdev-0.0.0.dev213.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
213
- omdev-0.0.0.dev213.dist-info/METADATA,sha256=9a3ViiPq2bMjH4oIJbymsj81T6dJuGB4ZAjgWVSdNDs,1638
214
- omdev-0.0.0.dev213.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
215
- omdev-0.0.0.dev213.dist-info/entry_points.txt,sha256=dHLXFmq5D9B8qUyhRtFqTGWGxlbx3t5ejedjrnXNYLU,33
216
- omdev-0.0.0.dev213.dist-info/top_level.txt,sha256=1nr7j30fEWgLYHW3lGR9pkdHkb7knv1U1ES1XRNVQ6k,6
217
- omdev-0.0.0.dev213.dist-info/RECORD,,
214
+ omdev-0.0.0.dev215.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
215
+ omdev-0.0.0.dev215.dist-info/METADATA,sha256=oNR4sVjlhZ4v-B5bp8h4XPFlDnI8aniDWCUq9WLk-ZM,1638
216
+ omdev-0.0.0.dev215.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
217
+ omdev-0.0.0.dev215.dist-info/entry_points.txt,sha256=dHLXFmq5D9B8qUyhRtFqTGWGxlbx3t5ejedjrnXNYLU,33
218
+ omdev-0.0.0.dev215.dist-info/top_level.txt,sha256=1nr7j30fEWgLYHW3lGR9pkdHkb7knv1U1ES1XRNVQ6k,6
219
+ omdev-0.0.0.dev215.dist-info/RECORD,,
omdev/ci/github/curl.py DELETED
@@ -1,209 +0,0 @@
1
- # ruff: noqa: UP006 UP007
2
- # @omlish-lite
3
- import dataclasses as dc
4
- import json
5
- import os
6
- import shlex
7
- import typing as ta
8
-
9
- from omlish.lite.check import check
10
- from omlish.lite.contextmanagers import defer
11
- from omlish.lite.json import json_dumps_compact
12
- from omlish.subprocesses import subprocesses
13
-
14
- from ..shell import ShellCmd
15
- from ..utils import make_temp_file
16
-
17
-
18
- ##
19
-
20
-
21
- class GithubServiceCurlClient:
22
- def __init__(
23
- self,
24
- service_url: str,
25
- auth_token: ta.Optional[str] = None,
26
- *,
27
- api_version: ta.Optional[str] = None,
28
- ) -> None:
29
- super().__init__()
30
-
31
- self._service_url = check.non_empty_str(service_url)
32
- self._auth_token = auth_token
33
- self._api_version = api_version
34
-
35
- #
36
-
37
- _MISSING = object()
38
-
39
- def build_headers(
40
- self,
41
- headers: ta.Optional[ta.Mapping[str, str]] = None,
42
- *,
43
- auth_token: ta.Any = _MISSING,
44
- content_type: ta.Optional[str] = None,
45
- ) -> ta.Dict[str, str]:
46
- dct = {
47
- 'Accept': ';'.join([
48
- 'application/json',
49
- *([f'api-version={self._api_version}'] if self._api_version else []),
50
- ]),
51
- }
52
-
53
- if auth_token is self._MISSING:
54
- auth_token = self._auth_token
55
- if auth_token:
56
- dct['Authorization'] = f'Bearer {auth_token}'
57
-
58
- if content_type is not None:
59
- dct['Content-Type'] = content_type
60
-
61
- if headers:
62
- dct.update(headers)
63
-
64
- return dct
65
-
66
- #
67
-
68
- HEADER_AUTH_TOKEN_ENV_KEY_PREFIX = '_GITHUB_SERVICE_AUTH_TOKEN' # noqa
69
-
70
- @property
71
- def header_auth_token_env_key(self) -> str:
72
- return f'{self.HEADER_AUTH_TOKEN_ENV_KEY_PREFIX}_{id(self)}'
73
-
74
- def build_cmd(
75
- self,
76
- method: str,
77
- url: str,
78
- *,
79
- json_content: bool = False,
80
- content_type: ta.Optional[str] = None,
81
- headers: ta.Optional[ta.Dict[str, str]] = None,
82
- ) -> ShellCmd:
83
- if content_type is None and json_content:
84
- content_type = 'application/json'
85
-
86
- env = {}
87
-
88
- header_auth_token: ta.Optional[str]
89
- if self._auth_token:
90
- header_env_key = self.header_auth_token_env_key
91
- env[header_env_key] = self._auth_token
92
- header_auth_token = f'${header_env_key}'
93
- else:
94
- header_auth_token = None
95
-
96
- built_hdrs = self.build_headers(
97
- headers,
98
- auth_token=header_auth_token,
99
- content_type=content_type,
100
- )
101
-
102
- url = f'{self._service_url}/{url}'
103
-
104
- cmd = ' '.join([
105
- 'curl',
106
- '-s',
107
- '-X', method,
108
- url,
109
- *[f'-H "{k}: {v}"' for k, v in built_hdrs.items()],
110
- ])
111
-
112
- return ShellCmd(
113
- cmd,
114
- env=env,
115
- )
116
-
117
- def build_post_json_cmd(
118
- self,
119
- url: str,
120
- obj: ta.Any,
121
- **kwargs: ta.Any,
122
- ) -> ShellCmd:
123
- curl_cmd = self.build_cmd(
124
- 'POST',
125
- url,
126
- json_content=True,
127
- **kwargs,
128
- )
129
-
130
- obj_json = json_dumps_compact(obj)
131
-
132
- return dc.replace(curl_cmd, s=f'{curl_cmd.s} -d {shlex.quote(obj_json)}')
133
-
134
- #
135
-
136
- @dc.dataclass()
137
- class Error(RuntimeError):
138
- status_code: int
139
- body: ta.Optional[bytes]
140
-
141
- def __str__(self) -> str:
142
- return repr(self)
143
-
144
- @dc.dataclass(frozen=True)
145
- class Result:
146
- status_code: int
147
- body: ta.Optional[bytes]
148
-
149
- def as_error(self) -> 'GithubServiceCurlClient.Error':
150
- return GithubServiceCurlClient.Error(
151
- status_code=self.status_code,
152
- body=self.body,
153
- )
154
-
155
- def run_cmd(
156
- self,
157
- cmd: ShellCmd,
158
- *,
159
- raise_: bool = False,
160
- **subprocess_kwargs: ta.Any,
161
- ) -> Result:
162
- out_file = make_temp_file()
163
- with defer(lambda: os.unlink(out_file)):
164
- run_cmd = dc.replace(cmd, s=f"{cmd.s} -o {out_file} -w '%{{json}}'")
165
-
166
- out_json_bytes = run_cmd.run(
167
- subprocesses.check_output,
168
- **subprocess_kwargs,
169
- )
170
-
171
- out_json = json.loads(out_json_bytes.decode())
172
- status_code = check.isinstance(out_json['response_code'], int)
173
-
174
- with open(out_file, 'rb') as f:
175
- body = f.read()
176
-
177
- result = self.Result(
178
- status_code=status_code,
179
- body=body,
180
- )
181
-
182
- if raise_ and (500 <= status_code <= 600):
183
- raise result.as_error()
184
-
185
- return result
186
-
187
- def run_json_cmd(
188
- self,
189
- cmd: ShellCmd,
190
- *,
191
- success_status_codes: ta.Optional[ta.Container[int]] = None,
192
- ) -> ta.Optional[ta.Any]:
193
- result = self.run_cmd(cmd, raise_=True)
194
-
195
- if success_status_codes is not None:
196
- is_success = result.status_code in success_status_codes
197
- else:
198
- is_success = 200 <= result.status_code < 300
199
-
200
- if is_success:
201
- if not (body := result.body):
202
- return None
203
- return json.loads(body.decode('utf-8-sig'))
204
-
205
- elif result.status_code == 404:
206
- return None
207
-
208
- else:
209
- raise result.as_error()