【问题标题】:requests.auth.AuthBase TypeError on callrequests.auth.AuthBase TypeError on call
【发布时间】:2021-07-14 08:43:53
【问题描述】:

来自https://docs.python-requests.org/en/master/user/authentication/的文档

我收集到我自己的 Auth 类中的 __call__ 函数应该有 r 参数,

但是,当我在 requests.get(auth=MyClass) 中调用此类时,我收到错误 TypeError: __call__() missing 1 required positional argument: 'r'

我的课程代码可以在这里找到https://pastebin.com/YDZ2DeaT

import requests
import time
import base64

from requests.auth import AuthBase

class TokenAuth(AuthBase):
    """Refreshes SkyKick token, for use with all Skykick requests"""
    def __init__(self, Username: str, SubKey: str):
        self.Username = Username
        self.SubKey = SubKey
    
    # Initialise with no token and instant expiry
        self.Token = None
        self.TokenExpiry = time.time()
    

        self.Headers = {
            # Request headers

            'Content-Type'             : 'application/x-www-form-urlencoded',
            'Ocp-Apim-Subscription-Key': self.SubKey,

        }

        self.Body = {
        # Request body
            'grant_type': 'client_credentials',
            'scope'     : 'Partner'
        }

    def regenToken(self):
    # Sends request to regenerate token
        try:
        # Get key from API
            response = requests.post("https://apis.skykick.com/auth/token",
                                     headers=self.Headers,
                                     auth=(self.Username, self.SubKey),
                                     data=self.Body,
                                     ).json()
        except: 
            raise Exception("Sending request failed, check connection.")

        # API errors are inconsistent, easiest way to catch them
        if "error" in response or "statusCode" in response:
            raise Exception(
                "Token requesting failed, cannot proceed with any Skykick actions, exiting.\n"
                f"Error raised was {response}")

    # Get token from response and set expiry
        self.Token = response["access_token"]
        self.TokenExpiry = time.time() + 82800

    def __call__(self, r):
    
    # If token expiry is now or in past, call regenToken
        if self.TokenExpiry <= time.time():
            self.regenToken()
    # Set headers and return complete requests.Request object
        r.headers["Authorization"] = f"Bearer {self.Token}"
        return r

# Initialise our token class, so it is ready to call
TokenClass = TokenAuth("test", "1234")

#Send request with class as auth method.
requests.get("https://apis.skykick.com/whoami", auth=TokenClass())

我已经尝试使用示例代码,它可以工作,但我无法弄清楚为什么我的代码不起作用。

python-requests 版本是 2.25.1

【问题讨论】:

  • 如果我创建一个空白 Request 对象,并将其传递到请求中,它可以工作,r = requests.Request() response = requests.get("https://apis.skykick.com/whoami", auth=TokenClass(r)) Traceback 这与文档显示用法的方式不同。
  • TokenClass = TokenAuth("test", "1234") 您似乎正在从一个类中创建一个对象,然后调用该对象并将其传入。您不应该只传入TokenClass 的实例吗?
  • @Alex028502 我不太确定有什么区别,你的意思是用 requests.get(auth=TokenAuth("test","1234")) 代替吗?

标签: python python-requests


【解决方案1】:

我想我知道发生了什么。

这行实例化了一个对象,叫做TokenClass

TokenClass = TokenAuth("test", "1234")

那么这里,

requests.get("https://apis.skykick.com/whoami", auth=TokenClass())

您正在像调用函数一样调用该对象

当您像函数一样调用对象时,python 会查找该对象的 __call__ 方法。

而且你没有在这里调用任何参数。你所拥有的和我认为的大致相同

requests.get("https://apis.skykick.com/whoami", auth=TokenClass.__call__())

因此它抱怨您缺少 r 参数


这是他们的例子:


import requests
class MyAuth(requests.auth.AuthBase):
    def __call__(self, r):
        # Implement my authentication
        return r

url = 'https://httpbin.org/get'
requests.get(url, auth=MyAuth())

MyAuth 是他们定义的一个类,然后MyAuth() 创建它的一个实例并将其传递给get

你的更像这样


import requests
class MyAuth(requests.auth.AuthBase):
    def __call__(self, r):
        # Implement my authentication
        return r

url = 'https://httpbin.org/get'

myAuth = MyAuth() # create an instance of the class

requests.get(url, auth=myAuth()) # call the instance and pass in result

也可以这样写


import requests
class MyAuth(requests.auth.AuthBase):
    def __call__(self, r):
        # Implement my authentication
        return r

url = 'https://httpbin.org/get'


requests.get(url, auth=MyAuth()())

此程序产生与您遇到的相同的错误

import requests
class MyAuth(requests.auth.AuthBase):
    def __call__(self, r):
        # Implement my authentication
        return r

url = 'https://httpbin.org/get'

MyAuth()()

因为当你把()放在一个类后面时,你得到一个实例,当你把()放在一个实例后面时,你调用__call__方法

【讨论】:

  • 您可以在docs.python-requests.org/en/master/user/authentication 的底部看到它的调用方式,我认为 r 参数应该由 requests 模块传递
  • 我明白了,我的方式依赖于现有的对象来保存令牌和到期,所以我想最简单的方法是创建自己的 Request 对象并使用它,或者存储令牌并找到方法快速检索。感谢您的洞察力!
  • 没有。当您将其实例化时,该对象存在 inline requests.get(url, auth=MyAuth()) 为了更好地理解,您应该将他们的示例更改为两行 myAuth=MyAuth(); requests.get(url, auth=myAuth)
  • 问题是你试图让它存在两次,但第一次,你使用 a 给你创建的对象一个类的名称(以大写字母开头),但它是真的是一个对象,可以按原样传入
  • 哦,我现在明白了!这对我有用,非常感谢! +1
猜你喜欢
  • 2016-09-29
  • 1970-01-01
  • 2017-08-02
  • 2022-12-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-22
  • 1970-01-01
相关资源
最近更新 更多