omlish 0.0.0.dev219__py3-none-any.whl → 0.0.0.dev220__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.
Files changed (36) hide show
  1. omlish/__about__.py +2 -2
  2. omlish/antlr/dot.py +13 -6
  3. omlish/dataclasses/__init__.py +1 -0
  4. omlish/dataclasses/impl/api.py +10 -0
  5. omlish/dataclasses/impl/fields.py +8 -1
  6. omlish/dataclasses/impl/init.py +1 -1
  7. omlish/dataclasses/impl/main.py +1 -1
  8. omlish/dataclasses/impl/metaclass.py +112 -29
  9. omlish/dataclasses/impl/overrides.py +53 -0
  10. omlish/dataclasses/impl/params.py +3 -0
  11. omlish/dataclasses/impl/reflect.py +17 -5
  12. omlish/dataclasses/impl/simple.py +0 -42
  13. omlish/http/coro/server.py +58 -38
  14. omlish/http/handlers.py +8 -0
  15. omlish/io/fileno.py +11 -0
  16. omlish/lang/__init__.py +4 -1
  17. omlish/lang/cached.py +0 -1
  18. omlish/lang/classes/__init__.py +3 -1
  19. omlish/lang/classes/abstract.py +14 -1
  20. omlish/lang/classes/restrict.py +5 -5
  21. omlish/lang/classes/virtual.py +0 -1
  22. omlish/lang/clsdct.py +0 -1
  23. omlish/lang/contextmanagers.py +0 -8
  24. omlish/lang/descriptors.py +0 -1
  25. omlish/lang/maybes.py +0 -1
  26. omlish/lang/objects.py +0 -2
  27. omlish/secrets/ssl.py +9 -0
  28. omlish/secrets/tempssl.py +50 -0
  29. omlish/sockets/bind.py +6 -1
  30. omlish/sockets/server/server.py +18 -5
  31. {omlish-0.0.0.dev219.dist-info → omlish-0.0.0.dev220.dist-info}/METADATA +1 -1
  32. {omlish-0.0.0.dev219.dist-info → omlish-0.0.0.dev220.dist-info}/RECORD +36 -32
  33. {omlish-0.0.0.dev219.dist-info → omlish-0.0.0.dev220.dist-info}/LICENSE +0 -0
  34. {omlish-0.0.0.dev219.dist-info → omlish-0.0.0.dev220.dist-info}/WHEEL +0 -0
  35. {omlish-0.0.0.dev219.dist-info → omlish-0.0.0.dev220.dist-info}/entry_points.txt +0 -0
  36. {omlish-0.0.0.dev219.dist-info → omlish-0.0.0.dev220.dist-info}/top_level.txt +0 -0
omlish/__about__.py CHANGED
@@ -1,5 +1,5 @@
1
- __version__ = '0.0.0.dev219'
2
- __revision__ = '5e741e0670fb75899653bd454587a1cf2add710f'
1
+ __version__ = '0.0.0.dev220'
2
+ __revision__ = '3af8855e48e09d340dd21835f45fe8a21a262fa9'
3
3
 
4
4
 
5
5
  #
omlish/antlr/dot.py CHANGED
@@ -1,12 +1,19 @@
1
+ import typing as ta
2
+
1
3
  from ..graphs import dot
2
4
  from . import runtime as antlr4
3
5
  from .utils import yield_contexts
4
6
 
5
7
 
6
- def dot_ctx(root: antlr4.ParserRuleContext) -> dot.Graph:
7
- stmts: list[dot.Stmt] = [
8
- dot.RawStmt('rankdir=LR;'),
9
- ]
8
+ def dot_ctx(
9
+ root: antlr4.ParserRuleContext,
10
+ *,
11
+ left_to_right: bool = False,
12
+ ) -> dot.Graph:
13
+ stmts: list[dot.Stmt] = []
14
+
15
+ if left_to_right:
16
+ stmts.append(dot.RawStmt('rankdir=LR;'))
10
17
 
11
18
  for c in yield_contexts(root):
12
19
  if isinstance(c, antlr4.TerminalNode):
@@ -27,5 +34,5 @@ def dot_ctx(root: antlr4.ParserRuleContext) -> dot.Graph:
27
34
  return dot.Graph(stmts)
28
35
 
29
36
 
30
- def open_dot_ctx(root: antlr4.ParserRuleContext) -> None:
31
- dot.open_dot(dot.render(dot_ctx(root)))
37
+ def open_dot_ctx(root: antlr4.ParserRuleContext, **kwargs: ta.Any) -> None:
38
+ dot.open_dot(dot.render(dot_ctx(root)), **kwargs)
@@ -67,6 +67,7 @@ from .impl.metaclass import ( # noqa
67
67
  DataMeta,
68
68
  Data,
69
69
  Frozen,
70
+ Case,
70
71
  Box,
71
72
  )
72
73
 
@@ -1,3 +1,7 @@
1
+ """
2
+ TODO:
3
+ - fix code redundancy
4
+ """
1
5
  import collections.abc
2
6
  import contextlib
3
7
  import dataclasses as dc
@@ -91,6 +95,7 @@ def dataclass( # noqa
91
95
  reorder=MISSING,
92
96
  cache_hash=MISSING,
93
97
  generic_init=MISSING,
98
+ override=MISSING,
94
99
  ):
95
100
  def wrap(cls):
96
101
  pkw = dict(
@@ -113,6 +118,7 @@ def dataclass( # noqa
113
118
  reorder=reorder,
114
119
  cache_hash=cache_hash,
115
120
  generic_init=generic_init,
121
+ override=override,
116
122
  )))
117
123
  pex = ParamsExtras(**epk)
118
124
 
@@ -161,6 +167,7 @@ def make_dataclass( # noqa
161
167
  reorder=MISSING,
162
168
  cache_hash=MISSING,
163
169
  generic_init=MISSING,
170
+ override=MISSING,
164
171
  ):
165
172
  if namespace is None:
166
173
  namespace = {}
@@ -221,6 +228,7 @@ def make_dataclass( # noqa
221
228
  reorder=reorder,
222
229
  cache_hash=cache_hash,
223
230
  generic_init=generic_init,
231
+ override=override,
224
232
  )
225
233
 
226
234
 
@@ -233,6 +241,7 @@ def extra_params( # noqa
233
241
  reorder=MISSING,
234
242
  cache_hash=MISSING,
235
243
  generic_init=MISSING,
244
+ override=MISSING,
236
245
  ):
237
246
  def inner(cls):
238
247
  if PARAMS_ATTR in cls.__dict__:
@@ -249,6 +258,7 @@ def extra_params( # noqa
249
258
  reorder=reorder,
250
259
  cache_hash=cache_hash,
251
260
  generic_init=generic_init,
261
+ override=override,
252
262
  ))
253
263
 
254
264
  return cls
@@ -165,6 +165,7 @@ def field_init(
165
165
  locals: dict[str, ta.Any], # noqa
166
166
  self_name: str,
167
167
  slots: bool,
168
+ cls_override: bool,
168
169
  ) -> ta.Sequence[str]:
169
170
  default_name = f'__dataclass_dflt_{f.name}__'
170
171
  fx = get_field_extras(f)
@@ -235,6 +236,12 @@ def field_init(
235
236
  )
236
237
 
237
238
  if value is not None and field_type(f) is not FieldType.INIT:
238
- lines.append(field_assign(frozen, f.name, value, self_name, fx.override)) # noqa
239
+ lines.append(field_assign(
240
+ frozen,
241
+ f.name,
242
+ value,
243
+ self_name,
244
+ fx.override or cls_override,
245
+ ))
239
246
 
240
247
  return lines
@@ -74,7 +74,6 @@ def init_param(f: dc.Field) -> str:
74
74
 
75
75
 
76
76
  class InitBuilder:
77
-
78
77
  def __init__(
79
78
  self,
80
79
  info: ClassInfo,
@@ -128,6 +127,7 @@ class InitBuilder:
128
127
  locals,
129
128
  self._self_name,
130
129
  self._info.params.slots,
130
+ self._info.params_extras.override,
131
131
  )
132
132
 
133
133
  if f_lines:
@@ -13,6 +13,7 @@ from .internals import FIELDS_ATTR
13
13
  from .internals import PARAMS_ATTR
14
14
  from .internals import Params
15
15
  from .order import OrderProcessor
16
+ from .overrides import OverridesProcessor
16
17
  from .params import ParamsExtras
17
18
  from .processing import Processor
18
19
  from .reflect import ClassInfo
@@ -21,7 +22,6 @@ from .repr import ReprProcessor
21
22
  from .simple import DocProcessor
22
23
  from .simple import EqProcessor
23
24
  from .simple import MatchArgsProcessor
24
- from .simple import OverridesProcessor
25
25
  from .slots import add_slots
26
26
 
27
27
 
@@ -9,6 +9,7 @@ import dataclasses as dc
9
9
  import typing as ta
10
10
 
11
11
  from ... import lang
12
+ from .api import MISSING
12
13
  from .api import dataclass
13
14
  from .api import field # noqa
14
15
  from .params import MetaclassParams
@@ -20,6 +21,28 @@ from .params import get_params_extras
20
21
  T = ta.TypeVar('T')
21
22
 
22
23
 
24
+ ##
25
+
26
+
27
+ _CONFER_PARAMS: tuple[str, ...] = (
28
+ 'frozen',
29
+ 'kw_only',
30
+ )
31
+
32
+ _CONFER_PARAMS_EXTRAS: tuple[str, ...] = (
33
+ 'reorder',
34
+ 'cache_hash',
35
+ 'generic_init',
36
+ 'override',
37
+ )
38
+
39
+ _CONFER_METACLASS_PARAMS: tuple[str, ...] = (
40
+ 'confer',
41
+ 'final_subclasses',
42
+ 'abstract_immediate_subclasses',
43
+ )
44
+
45
+
23
46
  def confer_kwarg(out: dict[str, ta.Any], k: str, v: ta.Any) -> None:
24
47
  if k in out:
25
48
  if out[k] != v:
@@ -44,22 +67,13 @@ def confer_kwargs(
44
67
  if ck in kwargs:
45
68
  continue
46
69
 
47
- if ck in (
48
- 'frozen',
49
- 'kw_only',
50
- ):
70
+ if ck in _CONFER_PARAMS:
51
71
  confer_kwarg(out, ck, getattr(get_params(base), ck))
52
72
 
53
- elif ck in (
54
- 'cache_hash',
55
- 'generic_init',
56
- 'reorder',
57
- ):
73
+ elif ck in _CONFER_PARAMS_EXTRAS:
58
74
  confer_kwarg(out, ck, getattr(get_params_extras(base), ck))
59
75
 
60
- elif ck in (
61
- 'confer',
62
- ):
76
+ elif ck in _CONFER_METACLASS_PARAMS:
63
77
  confer_kwarg(out, ck, getattr(bmp, ck))
64
78
 
65
79
  else:
@@ -68,6 +82,9 @@ def confer_kwargs(
68
82
  return out
69
83
 
70
84
 
85
+ ##
86
+
87
+
71
88
  class DataMeta(abc.ABCMeta):
72
89
  def __new__(
73
90
  mcls,
@@ -76,25 +93,21 @@ class DataMeta(abc.ABCMeta):
76
93
  namespace,
77
94
  *,
78
95
 
79
- # confer=frozenset(),
96
+ abstract=False,
97
+ sealed=False,
98
+ final=False,
80
99
 
81
100
  metadata=None,
82
101
  **kwargs,
83
102
  ):
84
- cls = lang.super_meta(
85
- super(),
86
- mcls,
87
- name,
88
- bases,
89
- namespace,
90
- )
91
-
92
103
  ckw = confer_kwargs(bases, kwargs)
93
104
  nkw = {**kwargs, **ckw}
94
105
 
95
- mcp = MetaclassParams(
96
- confer=nkw.pop('confer', frozenset()),
97
- )
106
+ mcp = MetaclassParams(**{
107
+ mpa: nkw.pop(mpa)
108
+ for mpa in _CONFER_METACLASS_PARAMS
109
+ if mpa in nkw
110
+ })
98
111
 
99
112
  mmd = {
100
113
  MetaclassParams: mcp,
@@ -104,13 +117,74 @@ class DataMeta(abc.ABCMeta):
104
117
  else:
105
118
  metadata = mmd
106
119
 
120
+ #
121
+
122
+ xbs: list[type] = []
123
+
124
+ if any(get_metaclass_params(b).abstract_immediate_subclasses for b in bases if dc.is_dataclass(b)):
125
+ abstract = True
126
+
127
+ final |= (mcp.final_subclasses and not abstract)
128
+
129
+ if final and abstract:
130
+ raise TypeError(f'Class cannot be abstract and final: {name!r}')
131
+
132
+ if abstract:
133
+ xbs.append(lang.Abstract)
134
+ if sealed:
135
+ xbs.append(lang.Sealed)
136
+ if final:
137
+ xbs.append(lang.Final)
138
+
139
+ if xbs:
140
+ if bases and bases[-1] is ta.Generic:
141
+ bases = (*bases[:-1], *xbs, bases[-1])
142
+ else:
143
+ bases = (*bases, *xbs)
144
+ if ob := namespace.get('__orig_bases__'):
145
+ if getattr(ob[-1], '__origin__', None) is ta.Generic:
146
+ namespace['__orig_bases__'] = (*ob[:-1], *xbs, ob[-1])
147
+ else:
148
+ namespace['__orig_bases__'] = (*ob, *xbs)
149
+
150
+ #
151
+
152
+ ofs: set[str] = set()
153
+ if any(issubclass(b, lang.Abstract) for b in bases) and nkw.get('override'):
154
+ ofs.update(a for a in namespace.get('__annotations__', []) if a not in namespace)
155
+ namespace.update((a, MISSING) for a in ofs)
156
+
157
+ #
158
+
159
+ cls = lang.super_meta(
160
+ super(),
161
+ mcls,
162
+ name,
163
+ bases,
164
+ namespace,
165
+ )
166
+
167
+ #
168
+
169
+ for a in ofs:
170
+ delattr(cls, a)
171
+
172
+ #
173
+
107
174
  return dataclass(cls, metadata=metadata, **nkw)
108
175
 
109
176
 
177
+ ##
178
+
179
+
110
180
  # @ta.dataclass_transform(field_specifiers=(field,)) # FIXME: ctor
111
181
  class Data(
112
182
  eq=False,
113
183
  order=False,
184
+ confer=frozenset([
185
+ 'confer',
186
+ 'final_subclasses',
187
+ ]),
114
188
  metaclass=DataMeta,
115
189
  ):
116
190
  def __init__(self, *args, **kwargs):
@@ -132,24 +206,33 @@ class Frozen(
132
206
  eq=False,
133
207
  order=False,
134
208
  confer=frozenset([
209
+ *get_metaclass_params(Data).confer,
135
210
  'frozen',
136
- 'cache_hash',
137
- 'confer',
138
211
  'reorder',
212
+ 'cache_hash',
213
+ 'override',
139
214
  ]),
140
215
  ):
141
216
  pass
142
217
 
143
218
 
219
+ class Case(
220
+ Frozen,
221
+ abstract=True,
222
+ override=True,
223
+ final_subclasses=True,
224
+ abstract_immediate_subclasses=True,
225
+ ):
226
+ pass
227
+
228
+
144
229
  class Box(
145
230
  Frozen,
146
231
  ta.Generic[T],
147
232
  generic_init=True,
148
233
  confer=frozenset([
149
- 'frozen',
150
- 'cache_hash',
234
+ *get_metaclass_params(Frozen).confer,
151
235
  'generic_init',
152
- 'confer',
153
236
  ]),
154
237
  ):
155
238
  v: T
@@ -0,0 +1,53 @@
1
+ from ... import lang
2
+ from .fields import field_assign
3
+ from .params import get_field_extras
4
+ from .processing import Processor
5
+ from .utils import create_fn
6
+ from .utils import set_new_attribute
7
+
8
+
9
+ class OverridesProcessor(Processor):
10
+ def _process(self) -> None:
11
+ for f in self._info.instance_fields:
12
+ fx = get_field_extras(f)
13
+ if not (fx.override or self._info.params_extras.override):
14
+ continue
15
+
16
+ if self._info.params.slots:
17
+ raise TypeError
18
+
19
+ self_name = '__dataclass_self__' if 'self' in self._info.fields else 'self'
20
+
21
+ getter = create_fn(
22
+ f.name,
23
+ (self_name,),
24
+ [f'return {self_name}.__dict__[{f.name!r}]'],
25
+ globals=self._info.globals,
26
+ return_type=lang.just(f.type),
27
+ )
28
+ prop = property(getter)
29
+
30
+ if not self._info.params.frozen:
31
+ setter = create_fn(
32
+ f.name,
33
+ (self_name, f'{f.name}: __dataclass_type_{f.name}__'),
34
+ [
35
+ field_assign(
36
+ self._info.params.frozen,
37
+ f.name,
38
+ f.name,
39
+ self_name,
40
+ True,
41
+ ),
42
+ ],
43
+ globals=self._info.globals,
44
+ locals={f'__dataclass_type_{f.name}__': f.type},
45
+ return_type=lang.just(None),
46
+ )
47
+ prop = prop.setter(setter)
48
+
49
+ set_new_attribute(
50
+ self._cls,
51
+ f.name,
52
+ prop,
53
+ )
@@ -91,6 +91,7 @@ class ParamsExtras(lang.Final):
91
91
  reorder: bool = False
92
92
  cache_hash: bool = False
93
93
  generic_init: bool = False
94
+ override: bool = False
94
95
 
95
96
 
96
97
  DEFAULT_PARAMS_EXTRAS = ParamsExtras()
@@ -110,6 +111,8 @@ def get_params_extras(obj: ta.Any) -> ParamsExtras:
110
111
  @dc.dataclass(frozen=True)
111
112
  class MetaclassParams:
112
113
  confer: frozenset[str] = frozenset()
114
+ final_subclasses: bool = False
115
+ abstract_immediate_subclasses: bool = False
113
116
 
114
117
 
115
118
  DEFAULT_METACLASS_PARAMS = MetaclassParams()
@@ -30,8 +30,20 @@ from .utils import Namespace
30
30
  MISSING = dc.MISSING
31
31
 
32
32
 
33
- class ClassInfo:
33
+ ##
34
+
35
+
36
+ def get_cls_annotations(cls: type) -> ta.Mapping[str, ta.Any]:
37
+ # Does not use ta.get_type_hints because that's what std dataclasses do [1]. Might be worth revisiting? A part
38
+ # of why they don't is to not import typing for efficiency but we don't care about that degree of startup speed.
39
+ # [1]: https://github.com/python/cpython/blob/54c63a32d06cb5f07a66245c375eac7d7efb964a/Lib/dataclasses.py#L985-L986 # noqa
40
+ return rfl.get_annotations(cls)
41
+
42
+
43
+ ##
34
44
 
45
+
46
+ class ClassInfo:
35
47
  def __init__(self, cls: type, *, _constructing: bool = False) -> None:
36
48
  check.isinstance(cls, type)
37
49
  self._constructing = _constructing
@@ -53,10 +65,7 @@ class ClassInfo:
53
65
 
54
66
  @cached.property
55
67
  def cls_annotations(self) -> ta.Mapping[str, ta.Any]:
56
- # Does not use ta.get_type_hints because that's what std dataclasses do [1]. Might be worth revisiting? A part
57
- # of why they don't is to not import typing for efficiency but we don't care about that degree of startup speed.
58
- # [1]: https://github.com/python/cpython/blob/54c63a32d06cb5f07a66245c375eac7d7efb964a/Lib/dataclasses.py#L985-L986 # noqa
59
- return rfl.get_annotations(self._cls)
68
+ return get_cls_annotations(self._cls)
60
69
 
61
70
  ##
62
71
 
@@ -157,6 +166,9 @@ class ClassInfo:
157
166
  return {k: rfl.to_annotation(v) for k, v in self.generic_replaced_field_types.items()}
158
167
 
159
168
 
169
+ ##
170
+
171
+
160
172
  _CLASS_INFO_CACHE: ta.MutableMapping[type, ClassInfo] = weakref.WeakKeyDictionary()
161
173
 
162
174
 
@@ -1,53 +1,11 @@
1
1
  import inspect
2
2
 
3
- from ... import lang
4
- from .fields import field_assign
5
3
  from .init import get_init_fields
6
- from .params import get_field_extras
7
4
  from .processing import Processor
8
5
  from .utils import create_fn
9
6
  from .utils import set_new_attribute
10
7
 
11
8
 
12
- class OverridesProcessor(Processor):
13
- def _process(self) -> None:
14
- for f in self._info.instance_fields:
15
- fx = get_field_extras(f)
16
- if not fx.override:
17
- continue
18
-
19
- if self._info.params.slots:
20
- raise TypeError
21
-
22
- self_name = '__dataclass_self__' if 'self' in self._info.fields else 'self'
23
-
24
- getter = create_fn(
25
- f.name,
26
- (self_name,),
27
- [f'return {self_name}.__dict__[{f.name!r}]'],
28
- globals=self._info.globals,
29
- return_type=lang.just(f.type),
30
- )
31
- prop = property(getter)
32
-
33
- if not self._info.params.frozen:
34
- setter = create_fn(
35
- f.name,
36
- (self_name, f'{f.name}: __dataclass_type_{f.name}__'),
37
- [field_assign(self._info.params.frozen, f.name, f.name, self_name, fx.override)],
38
- globals=self._info.globals,
39
- locals={f'__dataclass_type_{f.name}__': f.type},
40
- return_type=lang.just(None),
41
- )
42
- prop = prop.setter(setter)
43
-
44
- set_new_attribute(
45
- self._cls,
46
- f.name,
47
- prop,
48
- )
49
-
50
-
51
9
  class EqProcessor(Processor):
52
10
  def _process(self) -> None:
53
11
  if not self._info.params.eq:
@@ -205,6 +205,10 @@ class CoroHttpServer:
205
205
  return h
206
206
  return None
207
207
 
208
+ def close(self) -> None:
209
+ if isinstance(d := self.data, HttpHandlerResponseStreamedData):
210
+ d.close()
211
+
208
212
  #
209
213
 
210
214
  def _build_response_head_bytes(self, a: _Response) -> bytes:
@@ -420,35 +424,45 @@ class CoroHttpServer:
420
424
  while True:
421
425
  gen = self.coro_handle_one()
422
426
 
423
- o = next(gen)
424
427
  i: ta.Optional[bytes]
428
+ o: ta.Any = next(gen)
425
429
  while True:
426
- if isinstance(o, self.AnyLogIo):
427
- i = None
428
- yield o
430
+ try:
431
+ if isinstance(o, self.AnyLogIo):
432
+ i = None
433
+ yield o
429
434
 
430
- elif isinstance(o, self.AnyReadIo):
431
- i = check.isinstance((yield o), bytes)
435
+ elif isinstance(o, self.AnyReadIo):
436
+ i = check.isinstance((yield o), bytes)
432
437
 
433
- elif isinstance(o, self._Response):
434
- i = None
438
+ elif isinstance(o, self._Response):
439
+ i = None
435
440
 
436
- r = self._preprocess_response(o)
437
- hb = self._build_response_head_bytes(r)
438
- check.none((yield self.WriteIo(hb)))
441
+ r = self._preprocess_response(o)
442
+ hb = self._build_response_head_bytes(r)
443
+ check.none((yield self.WriteIo(hb)))
439
444
 
440
- for b in self._yield_response_data(r):
441
- yield self.WriteIo(b)
445
+ for b in self._yield_response_data(r):
446
+ yield self.WriteIo(b)
442
447
 
443
- else:
444
- raise TypeError(o)
448
+ o.close()
449
+ o = None
445
450
 
446
- try:
447
- o = gen.send(i)
448
- except EOFError:
449
- return
450
- except StopIteration:
451
- break
451
+ else:
452
+ raise TypeError(o) # noqa
453
+
454
+ try:
455
+ o = gen.send(i)
456
+ except EOFError:
457
+ return
458
+ except StopIteration:
459
+ break
460
+
461
+ except Exception: # noqa
462
+ if hasattr(o, 'close'):
463
+ o.close()
464
+
465
+ raise
452
466
 
453
467
  def coro_handle_one(self) -> ta.Generator[
454
468
  ta.Union[AnyLogIo, AnyReadIo, _Response],
@@ -530,28 +544,34 @@ class CoroHttpServer:
530
544
  yield self._build_error_response(err)
531
545
  return
532
546
 
533
- # Build internal response
547
+ try:
548
+ # Build internal response
534
549
 
535
- response_headers = handler_response.headers or {}
536
- response_data = handler_response.data
550
+ response_headers = handler_response.headers or {}
551
+ response_data = handler_response.data
537
552
 
538
- headers: ta.List[CoroHttpServer._Header] = [
539
- *self._make_default_headers(),
540
- ]
553
+ headers: ta.List[CoroHttpServer._Header] = [
554
+ *self._make_default_headers(),
555
+ ]
541
556
 
542
- for k, v in response_headers.items():
543
- headers.append(self._Header(k, v))
557
+ for k, v in response_headers.items():
558
+ headers.append(self._Header(k, v))
544
559
 
545
- if handler_response.close_connection and 'Connection' not in headers:
546
- headers.append(self._Header('Connection', 'close'))
560
+ if handler_response.close_connection and 'Connection' not in headers:
561
+ headers.append(self._Header('Connection', 'close'))
547
562
 
548
- yield self._Response(
549
- version=parsed.version,
550
- code=http.HTTPStatus(handler_response.status),
551
- headers=headers,
552
- data=response_data,
553
- close_connection=handler_response.close_connection,
554
- )
563
+ yield self._Response(
564
+ version=parsed.version,
565
+ code=http.HTTPStatus(handler_response.status),
566
+ headers=headers,
567
+ data=response_data,
568
+ close_connection=handler_response.close_connection,
569
+ )
570
+
571
+ except Exception: # noqa
572
+ handler_response.close()
573
+
574
+ raise
555
575
 
556
576
 
557
577
  ##