网络适配器传输协议_编写传输适配器

本文介绍了如何在Python Requests库v1.0中使用传输适配器,包括适配器的基本结构、实现FTP传输适配器的详细步骤以及如何将其整合到请求会话中,提供了一个实现FTP新功能的示例。
摘要由CSDN通过智能技术生成

网络适配器传输协议

Last post I briefly mentioned that Python Requests went v1.0. This involved a huge code refactor and a few changes in the API.

我在上一篇文章中简要提到了Python Requests v1.0。 这涉及大量的代码重构和API中的一些更改。

One of these changes was the inclusion of something Kenneth (and others) have been thinking about for a while as part of the future of Python HTTP ‘project’. This something is the ‘Transport Adapter’. From Kenneth’s blog post:

这些更改之一是包含了Kenneth(和其他人)已经考虑了一段时间的某些事情,这是Python HTTP “项目” 未来的一部分。 这就是“运输适配器”。 从肯尼斯的博客文章中:

Transport adapters will provide a mechanism to define interaction methods for an “HTTP” service. They will allow you to fully mock a web service to fit your needs.

传输适配器将提供一种机制来定义“ HTTP”服务的交互方法。 他们将允许您完全模拟Web服务以满足您的需求。

Well, v1.0 brought the inclusion of Transport Adapters into Requests. This is a shiny new feature, and I wanted to take some time to explore it. The best way to do that is to go ahead and write an adapter! As well as providing a useful bit of experience for me, I would be able to document the process and thereby provide a bit of a tutorial for anyone who wants to follow in my footsteps.

好吧,v1.0将传输适配器包含在请求中。 这是一个闪亮的新功能,我想花一些时间来探索它。 最好的方法是继续编写适配器! 除了为我提供有用的经验之外,我还将能够记录该过程,从而为想要跟随我的足迹的任何人提供一些教程。

But what to write an adapter for? I wanted to see how far Kenneth’s goal of Transport Adapters could be stretched, so I decided to implement a transport adapter not just for a specific web service, but for a whole new protocol: FTP.

但是,为适配器写些什么呢? 我想看看肯尼思(Kenneth)的传输适配器的目标可以扩展到什么程度,所以我决定不仅为特定的Web服务,而且为全新的协议:FTP实施传输适配器。

Come with me, as I show you how to write an adapter through the lens of my own.

跟我来,正如我向您展示如何通过我自己的镜头编写适配器。

For those who just want to see my work, you can see the finished product here.

对于只想看我作品的人,您可以在这里看到成品。

入门 (Getting Started)

The first thing to do is to work out how you implement such a thing! A quick look at the Requests code should tell you the answer. A Transport Adapter should be a subclass of requests.adapters.BaseAdapter, and should implement three methods: __init__(), send() and close(). Let’s take a closer look at these.

首先要做的就是弄清楚如何实现这种东西! 快速浏览“请求”代码将告诉您答案。 传输适配器应该是子类requests.adapters.BaseAdapter ,并应实现三个方法: __init__() send()close() 。 让我们仔细看看这些。

First, __init__(). This will be called only once, when your adapter is instantiated. This means that any state that should be common to all requests belongs here. As an example, Requests’ included HTTP Transport Adapter creates a urllib3 ConnectionPool object here. In my case, all I wanted to do was allow multiple FTP verbs, so I created an ad-hoc function table keyed off the names of the verbs. Pretty hacky, but fast.

首先, __init__() 。 实例化适配器时,它将仅被调用一次。 这意味着所有请求应共有的任何状态都属于此处。 例如,请求的随附HTTP传输适配器在此处创建urllib3 ConnectionPool对象。 就我而言,我只想允许多个FTP动词,所以我创建了一个临时函数表,以这些动词的名称为键。 很hacky,但是速度很快。

Second, close(). This is also only called once, at the exact opposite moment to __init__(). Any state you set up in init() should be torn down here. Again, for Requests’ built-in adapters, this means tearing down their ConnectionPools. For me, nothing.

其次, close() 。 在与__init__()完全相反的时刻,也仅调用一次。 您在init()设置的任何状态都应在此处拆除。 同样,对于请求的内置适配器,这意味着需要拆除其ConnectionPool 。 对我来说,什么都没有。

Finally, send(). This is where the meat of your functionality will occur, so let’s take some time over it.

最后, send() 。 这就是功能的关键所在,所以让我们花一些时间。

发送 (Send)

Glancing through the Requests code reveals that the send() function must, at minimum, accept a single PreparedRequest object and a list of kwargs, and return a Response object. This is where I began to run into trouble.

浏览Requests代码可以发现send()函数必须至少接受一个PreparedRequest对象和一个kwargs列表,并返回Response对象。 这就是我开始遇到麻烦的地方。

By default, the PreparedRequest object is very HTTP oriented. This is by design, and any transport adapter you write is likely to be happy with that. However, for me it was mildly problematic. In particular, the PreparedRequest contains almost none of the information on the standard Request object. It carries a url, a method, its headers, its body and its hooks. That’s it.

默认情况下, PreparedRequest对象非常面向HTTP。 这是设计使然,您编写的任何传输适配器都可能对此感到满意。 但是,对我来说,这是个小问题。 特别是, PreparedRequest几乎不包含有关标准Request对象的信息。 它带有一个url ,一个method ,其headers ,其body和其hooks 。 而已。

You get a little more detail from the kwargs. You get the values of the following Requests fields: stream, timeout, verify, cert and proxies. Mostly this stuff is of minimal value, and I ended up ignoring it (though I may go back and use timeout.)

您可以从kwargs获得更多细节。 您将获得以下“请求”字段的值: streamtimeoutverifycertproxies 。 通常,这些东西的价值很小,我最终忽略了它(尽管我可能会回过头来使用timeout 。)

If you’re using HTTP, I suspect you can mostly get by with this. Byte-level manipulation of the body is possible (and encouraged), so you can permute the Request into whatever form is expected by your web service.

如果您使用的是HTTP,我怀疑您基本上可以通过此方法来解决。 主体的字节级操作是可能的(并鼓励),因此您可以将请求置换为Web服务期望的任何形式。

For FTP, this was harder. Mostly I wasn’t interested in the data here, except when running an FTP STOR. Here, I wanted to piggyback on Requests’ current file upload syntax. However, by the time I get hold of the Prepared Request, the file has been encoded as multipart form-data. My current solution is to use the Python CGI module to decode the data again. This is shoddy, and far and away the worst part of the code I’ve written.

对于FTP,这很难。 通常,除了运行FTP STOR时,我对这里的数据不感兴趣。 在这里,我想搭载Requests当前的文件上传语法。 但是,当我收到“ Prepared Request ,该文件已被编码为多部分表单数据。 我当前的解决方案是使用Python CGI模块再次解码数据。 这是伪劣的,而且是我编写的代码中最糟糕的部分。

Returning to your code, this function has all of the responsibility for both sending the data out and parsing it back into a useful form for Requests. You may find, as I did, that you actually want to split this out into several functions or methods.

返回您的代码,此函数负责发送数据并将其解析回有用的形式以用于请求。 您可能会像我一样发现您实际上想将其拆分为几个函数或方法。

When building a response, it’s particularly instructive to take a look at the function build_response in requests.adapters. This gives you an idea of the kinds of fields Requests expects to be filled in. I ended up basing my equivalent function very heavily on this.

当建立一个响应,这是特别有启发看看在函数build_responserequests.adapters 。 这使您对Requests期望填写的字段类型有所了解。我最终在很大程度上基于我的等效功能。

惊喜 (Surprises)

There are a few surprises to be found. The first is that response.content and response.text only work if you provide a file-like object as r.raw. I ended up using BytesIO for this, which is a pain. If you’re using urllib3 this should be easy for you, but it’s worth knowing.

有一些惊喜。 首先是response.contentresponse.text仅在您提供类似文件的对象r.raw 。 我最终为此使用了BytesIO,这很痛苦。 如果您使用的是urllib3这对您来说应该很容易,但这是值得知道的。

Also bear in mind that you may have to jump through some hoops to get very Requests-like behaviour. The special case of Basic Auth as a naked tuple is handled above the PreparedRequest layer, so if you want to piggyback on it (like I did) be prepared to mess about with headers to get it to work.

还请记住,您可能必须跳过一些障碍才能获得非常类似于“请求”的行为。 Basic Auth作为裸元组的特例是在PreparedRequest层之上处理的,因此,如果您想要在其上搭载(如我一样),请准备好使用标头使它工作。

插入 (Plugging it in)

Once you’ve got a transport adapter, all you need to do is plug it in. To do this, use the mount method of the Session object:

获得传输适配器后,只需插入即可。为此,请使用Session对象的mount方法:

s s = = requestsrequests .. SessionSession ()
()
ss .. mountmount (( 'http://randomservice''http://randomservice' , , YourAdapterYourAdapter ())())

In my case, this was actually

就我而言,这实际上是

Any subsequent Request through that session will use your transport adapter if the URL prefix matches!

如果URL前缀匹配,则该会话中的所有后续请求都将使用您的传输适配器!

深入 (Going Deep)

For my stuff, though, I wanted to go a bit further. In particular, since I was adding new verbs (LIST, STOR, RETR and NLST), I wanted to make the utility methods available: the equivalents of Session.get() and Session.post(). To do this required that I monkeypatch the Session object.

不过,对于我的东西,我想进一步介绍。 特别是,由于我要添加新的动词(LIST,STOR,RETR和NLST),所以我想使实用程序方法可用: Session.get()Session.post()的等效项。 为此,我需要对Session对象进行monkeypatch。

I didn’t want to do this on import, though: the end user should have the choice about whether they actually want me to mangle the Session object or not. For this reason, I export a function: monkeypatch_session(). This function adds four utility functions to the Session object and overrides the constructor so that the FTPAdapter is automatically mounted.

但是,我不想在导入时执行此操作:最终用户应该选择是否实际上要我处理Session对象。 因此,我导出一个函数: monkeypatch_session() 。 此函数将四个实用程序函数添加到Session对象,并覆盖构造函数,以便自动安装FTPAdapter

现在都在一起了! (All Together Now!)

When you put that all together, you get my shiny new library. You can get this from the cheeseshop: just run pip install requests-ftp. Once you’ve got it, you can go straight into using it! For example, you can list the files at the root of the directory and then get one:

当您将所有内容放在一起时,就会得到我崭新的图书馆 。 您可以从Cheeseshop商店获得:只需运行pip install requests-ftp 。 一旦掌握了它,就可以直接使用它! 例如,您可以在目录的根目录列出文件,然后得到一个文件:

import import requests
requests
import import requests_ftp requests_ftp as as ftp
ftp
ftpftp .. monkeypatch_sessionmonkeypatch_session ()

()

with with requestsrequests .. SessionSession () () as as ss :
    :
    r r = = ss .. listlist (( 'ftp://127.0.0.1/''ftp://127.0.0.1/' , , authauth == (( 'username''username' , , 'password''password' ))
    ))
    file file = = rr .. contentcontent .. splitsplit (( '' nn '' )[)[ 00 ]
    ]
    r r = = ss .. retrretr (( 'ftp://127.0.0.1/' 'ftp://127.0.0.1/' + + filefile , , authauth == (( 'username''username' , , 'password''password' ))
    ))
    file file = = openopen (( 'temp.txt''temp.txt' , , 'wb''wb' )
    )
    filefile .. writewrite (( rr .. contentcontent ))

注意事项 (Caveats)

There are lots of things my code doesn’t do. A list can be found on the readme. If you feel like you want to use it anyway, feel free! I’d also welcome contributions to resolve some of its more significant problems (like the fact it isn’t tested).

我的代码有很多不能做的事情。 在自述文件中可以找到一个列表。 如果您仍然想使用它,请放心! 我也欢迎您为解决其一些更重要的问题(例如未经测试的事实)做出贡献。

加起来 (Summing Up)

翻译自: https://www.pybloggers.com/2012/12/writing-a-transport-adapter/

网络适配器传输协议

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值