如何创建通过items()返回重复值的dictlike对象

2024-06-01 05:43:19 发布

您现在位置:Python中文网/ 问答频道 /正文

框架

我正在使用hyper框架生成HTTP/2流量。在生成请求和响应时,我当前使用hyper.HTTP20Connection.requesth2.H2Connection.send_headers分别发送HTTP/2请求和响应

我的要求

我需要能够发送带有重复字段的HTTP/2请求和响应。例如,这里是一个YAML指定的请求,它包含两个x-test-duplicate字段:

  headers:
    fields:
    - [ :method, GET, equal ]
    - [ :scheme, https, equal ]
    - [ :authority, example.data.com, equal ]
    - [ :path, '/a/path?q=3', equal ]
    - [ Accept, '*/*' ]
    - [ Accept-Language, en-us ]
    - [ Accept-Encoding, gzip ]
    - [ x-test-duplicate, first ]
    - [ x-test-duplicate, second ]
    - [ Content-Length, "0" ]

注意,根据HTTP/2规范,这是明确允许的。例如,见RFC 7541 section 2.3.2

The dynamic table can contain duplicate entries (i.e., entries with the same name and same value). Therefore, duplicate entries MUST NOT be treated as an error by a decoder.

我的问题

问题是h2.H2Connection.send_headers正确地处理可能包含重复字段的元组的iterable(例如,(("name1", "value1"), ("name2", "value2"), ("name1", "another_value"))),hyper.HTTP20Connection.request需要一个字典,当然,它不是为重复键设计的。文档不清楚headers上的类型要求,但是在HTTP20Connection:request, line 261的源代码中,items()被取消了。如果我传递一个元组的iterable,我得到AttributeError: 'tuple' object has no attribute 'items'。请注意,这是多么可悲:超框架强制用户传入一个不允许重复的字典,然后立即通过items()将该字典转换为元组的一个iterable,后者允许重复字段。如果只需要一组元组,比如h2的接口,我就不会有这个问题

我的问题

我在hyper github项目中提交了关于此限制的问题437。同时,我希望我能解决这个问题。我有一个表示HTTP/2头的元组表,其中包含重复的字段。我可以用某种方式把它包装在一个对象中,这样当HTTP20Connection:request, line 261对它调用items()时,它只返回元组的iterable吗


Tags: test框架httprequestitemsh2equaliterable
2条回答

Python通常为此(docs)使用一个EmailMessage实例

The EmailMessage dictionary-like interface is indexed by the header names, which must be ASCII values. The values of the dictionary are strings with some extra methods. Headers are stored and returned in case-preserving form, but field names are matched case-insensitively. Unlike a real dict, there is an ordering to the keys, and there can be duplicate keys. Additional methods are provided for working with headers that have duplicate keys.

在基本用法中,您可以使用^{}设置项,并使用^{}检索它们。您可以将有效负载留空

>>> from email.message import EmailMessage
>>> headers = EmailMessage()
>>> headers.add_header("x-test-duplicate", "first")
>>> headers.add_header("x-test-duplicate", "second")
>>> headers.items()
[('x-test-duplicate', 'first'), ('x-test-duplicate', 'second')]

这是在{a4}本身进行的战斗测试,而{a5}

与滚动自己的multidict或使用简单的对列表相比,其优点是可以获得标题的正确行为(RFC 5322和RFC 6532样式字段名和值),例如不区分大小写:

>>> headers.add_header("aBc", "val1")
>>> headers.add_header("AbC", "val2")
>>> headers.get_all("ABC")
['val1', 'val2']

由于Python支持Duck类型,因此具有items方法的类就足够了:

class Wrapper:
    __slots__ = ["pairs"]  # Minor optimization
    def __init__(self, pairs: Iterable[Tuple[str, str]]):
        self.pairs = pairs

    def items(self) -> Iterable[Tuple[str, str]]:
        return self.pairs

然后:

header_pairs = [("name1", "value1"), ("name2", "value2"), ("name1", "another_value")]
wrapped = Wrapper(header_pairs)
request(method, url, body, headers=wrapped)

这方面的主要问题是它完全依赖于request的实现细节。如果他们将来选择将headers用于其他类似dict的目的,这将很容易打破

相关问题 更多 >