Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

have a little magic #288

Closed
wants to merge 5 commits into from
Closed

have a little magic #288

wants to merge 5 commits into from

Conversation

jingyuexing
Copy link

来点魔法

    1. {新增} 新增@Request装饰器
@Request(method="GET",url="https://www.bilibili.com/")
def getBilibiliHomePage(response=None,**kw):  # 参数最好是这种形式 或者只保留`**kw`这种形式
      if(response.status_code == 200):
            return response.content.decode()

homepage = getBilibiliHomePage()

@jingyuexing
Copy link
Author

具体详情 请参考我的另外一个库httpx

@jingyuexing
Copy link
Author

jingyuexing commented May 16, 2023

你可以在路径当中填写{name} 或者 :id 这种形式的字符串进行占位

@Request(method="GET",url="https://space.bilibili.com/:uid") # 或者 将:uid 替换成 {uid} 这种形式
def getUserSpacePage(response=None,**kw):  # 参数最好是这种形式 或者只保留`**kw`这种形式
      if(response.status_code == 200):
            return response.content.decode()

homepage = getBilibiliHomePage(path={"uid":14531767})

@Drelf2018
Copy link
Collaborator

Drelf2018 commented May 16, 2023

不太理解这个修饰器作用,对于下面这个函数

def check_qrcode_events(login_key) -> Tuple[QrCodeLoginEvents, Union[str, Credential]]:
"""
检查登录状态。(建议频率 1s,这个 API 也有风控!)
Args:
login_key (str): 登录密钥(get_qrcode 的返回值第二项)
Returns:
Tuple[QrCodeLoginEvents, str|Credential]: 状态(第一项)和信息(第二项)(如果成功登录信息为凭据类)
"""
events_api = API["qrcode"]["get_events"]
params = {"qrcode_key": login_key}
events = json.loads(
requests.get(
events_api["url"],
params=params,
cookies={"buvid3": str(uuid.uuid1()), "Domain": ".bilibili.com"},
).text
)
if "code" in events.keys() and events["code"] == -412:
raise LoginError(events["message"])
if events["data"]["code"] == 86101:
return QrCodeLoginEvents.SCAN, events["message"]
elif events["data"]["code"] == 86090:
return QrCodeLoginEvents.CONF, events["message"]
elif events["data"]["code"] == 0:
url: str = events["data"]["url"]
cookies_list = url.split("?")[1].split("&")
sessdata = ""
bili_jct = ""
dede = ""
for cookie in cookies_list:
if cookie[:8] == "SESSDATA":
sessdata = cookie[9:]
if cookie[:8] == "bili_jct":
bili_jct = cookie[9:]
if cookie[:11].upper() == "DEDEUSERID=":
dede = cookie[11:]
c = Credential(sessdata, bili_jct, dedeuserid=dede)
return QrCodeLoginEvents.DONE, c
else:
raise Exception()

是可以改写成这样吗?

@Request(method="GET", url=API["qrcode"]["get_events"]["url"], cookies={"buvid3": str(uuid.uuid1()), "Domain": ".bilibili.com"})
def check_qrcode_events(response=None, **kw):
    events = json.loads(response.content)
    # 以下省略

check_qrcode_events(params={"qrcode_key": login_key})

@Drelf2018
Copy link
Collaborator

另外这个装饰器似乎会把同步函数改成异步的?

@jingyuexing
Copy link
Author

另外这个装饰器似乎会把同步函数改成异步的?

因为使用的是异步的请求

@jingyuexing
Copy link
Author

不太理解这个修饰器作用,对于下面这个函数

def check_qrcode_events(login_key) -> Tuple[QrCodeLoginEvents, Union[str, Credential]]:
"""
检查登录状态。(建议频率 1s,这个 API 也有风控!)
Args:
login_key (str): 登录密钥(get_qrcode 的返回值第二项)
Returns:
Tuple[QrCodeLoginEvents, str|Credential]: 状态(第一项)和信息(第二项)(如果成功登录信息为凭据类)
"""
events_api = API["qrcode"]["get_events"]
params = {"qrcode_key": login_key}
events = json.loads(
requests.get(
events_api["url"],
params=params,
cookies={"buvid3": str(uuid.uuid1()), "Domain": ".bilibili.com"},
).text
)
if "code" in events.keys() and events["code"] == -412:
raise LoginError(events["message"])
if events["data"]["code"] == 86101:
return QrCodeLoginEvents.SCAN, events["message"]
elif events["data"]["code"] == 86090:
return QrCodeLoginEvents.CONF, events["message"]
elif events["data"]["code"] == 0:
url: str = events["data"]["url"]
cookies_list = url.split("?")[1].split("&")
sessdata = ""
bili_jct = ""
dede = ""
for cookie in cookies_list:
if cookie[:8] == "SESSDATA":
sessdata = cookie[9:]
if cookie[:8] == "bili_jct":
bili_jct = cookie[9:]
if cookie[:11].upper() == "DEDEUSERID=":
dede = cookie[11:]
c = Credential(sessdata, bili_jct, dedeuserid=dede)
return QrCodeLoginEvents.DONE, c
else:
raise Exception()

是可以改写成这样吗?

@Request(method="GET", url=API["qrcode"]["get_events"]["url"], cookies={"buvid3": str(uuid.uuid1()), "Domain": ".bilibili.com"})
def check_qrcode_events(response=None, **kw):
    events = json.loads(response.content)
    # 以下省略

check_qrcode_events(params={"qrcode_key": login_key})

你的理解是正确的

@jingyuexing
Copy link
Author

当你在调用的时候 就会给你的装饰函数传入一个响应对象,这是请求完了之后的response,你可以把要处理的操作放在这个函数里面
你可以选择有返回值或者没有返回值,根据你自己的需求做决定. 具体的对于请求的结果的处理,你在装饰的函数里面处理就行了,至于发起网络请求 不需要你自己去关心.装饰器会帮你处理好

@Drelf2018
Copy link
Collaborator

  • @dataclass 化简了原先的 __init__()
  • iscoroutinefunction 判断被修饰函数同步/异步,并返回相应函数,保证函数同步性不变
from dataclasses import dataclass
from inspect import iscoroutinefunction as isAsync
from typing import Any, Callable, Dict, Optional

import httpx
from aiohttp import CookieJar


@dataclass
class Request:
    "请求装饰器"

    url: str
    method: str
    timeout: Optional[int] = None
    cookies: Optional[CookieJar] = None
    path: Optional[Dict[str, Any]] = None
    body: Optional[Dict[str, Any]] = None
    form: Optional[Dict[str, Any]] = None
    json: Optional[Dict[str, Any]] = None
    query: Optional[Dict[str, Any]] = None
    headers: Optional[Dict[str, Any]] = None

    def __post_init__(self):
        self.cookies = self.covernCookies(self.cookies)

    def formatURLPath(self, url: str, data: Optional[Dict[str, Any]] = None):
        if data is not None:
            backupURL = url
            for key in data:
                backupURL = backupURL.replace("{%s}"%(key), str(data.get(key)), 1)
                backupURL = backupURL.replace(":%s"%(key), str(data.get(key)), 1)
            return backupURL
        return url

    def covernCookies(self, cookies: Optional[Dict[str, Any]] = None):
        if cookies is not None:
            # TODO: implement dict to cookiesJar
            cookiejarInstance = CookieJar()
            return cookiejarInstance

    def update(self, **kwargs):
        "更新参数"

        self.url = kwargs.get("url", self.url)
        self.path = kwargs.get("path", self.path)
        self.method = kwargs.get("method", self.method)
        overrideHeaders: Optional[Dict[str,Any]] = kwargs.get("headers")
        if overrideHeaders is not None:
            self.headers = self.headers or {}
            for key in overrideHeaders:
                self.headers[key] = overrideHeaders[key]
        self.headers = kwargs.get("headers", self.headers)
        self.query = kwargs.get("query", self.query)
        self.json = kwargs.get("json", self.json)

    def __call__(self, func: Callable):
        if isAsync(func):
            async def wapper(*args, **kwargs):
                self.update(kwargs=kwargs)
                session =  get_session()
                res = await session.request(
                    method=self.method,
                    url=self.formatURLPath(self.url, self.path),
                    json=self.json,
                    headers=self.headers,
                    params=self.query,
                    timeout=self.timeout
                )
                return await func(response=res, *args, **kwargs)
            return wapper
        else:
            def wapper(*args, **kwargs):
                self.update(kwargs=kwargs)
                res = httpx.request(
                    method=self.method,
                    url=self.formatURLPath(self.url, self.path),
                    json=self.json,
                    headers=self.headers,
                    params=self.query,
                    timeout=self.timeout
                ) 
                return func(response=res, *args, **kwargs)
            return wapper

@z0z0r4
Copy link
Collaborator

z0z0r4 commented May 17, 2023

你可以在路径当中填写{name} 或者 :id 这种形式的字符串进行占位

@Request(method="GET",url="https://space.bilibili.com/:uid") # 或者 将:uid 替换成 {uid} 这种形式
def getUserSpacePage(response=None,**kw):  # 参数最好是这种形式 或者只保留`**kw`这种形式
      if(response.status_code == 200):
            return response.content.decode()

homepage = getBilibiliHomePage(path={"uid":14531767})

如何实现自动补全...不便于开发

@z0z0r4
Copy link
Collaborator

z0z0r4 commented May 17, 2023

变成 path={"uid":14531767} 的话,封装的意义呢...

@jingyuexing
Copy link
Author

可以使用BaseModel进行自动补全

@jingyuexing
Copy link
Author

变成 path={"uid":14531767} 的话,封装的意义呢...

@jingyuexing
Copy link
Author

变成 path={"uid":14531767} 的话,封装的意义呢...

如果不提供更改注解当中设置好的东西,会导致直接就把参数写死,所以后续调用的时候定义好的东西都是可以更改的,包括方法,url等等,如果想活得类型自动补全 完全可以定义继承自BaseModel的模型

@Drelf2018
Copy link
Collaborator

给个例子吧,我感觉用处就是:需要先请求别的接口获取数据后,再利用返回值请求另一个接口。

@jingyuexing
Copy link
Author

给个例子吧,我感觉用处就是:需要先请求别的接口获取数据后,再利用返回值请求另一个接口。

其实你也完全可以在封装好的函数里面处理完了 直接返回结果的

@z0z0r4
Copy link
Collaborator

z0z0r4 commented May 19, 2023

暂时没空处理这个...

@z0z0r4 z0z0r4 added the GUGU label May 19, 2023
@Drelf2018
Copy link
Collaborator

可以merge,但是可能用处不大

@Drelf2018
Copy link
Collaborator

@z0z0r4 把这个merge了然后发release吧

@z0z0r4
Copy link
Collaborator

z0z0r4 commented May 27, 2023

Style Format

@dataclass
class Request:
    '''
    Request 装饰器

    用于代替直接调用异步函数 request
    '''
    url: str
    method: str
    path: Optional[Dict[str, Any]] = None
    query: Optional[Dict[str, Any]] = None
    body: Optional[Dict[str, Any]] = None
    form: Optional[Dict[str, Any]] = None
    cookies: Optional['CookieJar'] = None
    json: Optional[Dict[str, Any]] = None
    timeout: Optional[int] = None
    headers: Optional[Dict[str, Any]] = None

    def format_url_path(self, url: str, data: Optional[Dict[str, Any]] = None):
        '''
        格式化 URL
        '''
        if data is not None:
            backup_url = url
            for key in data:
                backup_url = backup_url.replace(
                    "{%s}" % (key), str(data.get(key)), 1)
                backup_url = backup_url.replace(
                    ":%s" % (key), str(data.get(key)), 1)
            return backup_url
        return url

    def convert_Cookies(self, cookies: Optional[Dict[str, Any]] = None):
        '''
        将 Cookie 转换为 CookieJar
        '''
        if cookies != None:
            # TODO: implement dict to cookieJar
            cookiejarInstance = CookieJar()
            return cookiejarInstance

    def update(self, **kwargs):
        '''
        更新参数
        '''
        self.url = kwargs.get("url", self.url)
        self.path = kwargs.get("path", self.path)
        self.method = kwargs.get("method", self.method)
        override_headers: Optional[Dict[str, Any]] = kwargs.get("headers")
        if override_headers is not None:
            self.headers = self.headers or {}
            for key in override_headers:
                self.headers[key] = override_headers[key]
        self.query = kwargs.get("query", self.query)
        self.json = kwargs.get("json", self.json)

    def __call__(self, func: Callable):
        if isAsync(func):
            async def wapperAsync(*args, **kwargs):
                session = get_session()
                self.update(kwargs=kwargs)
                if self.method == "ws" or self.method == "websocket":
                    with connect(self.url) as websocket:
                        websocket.send(self.body or self.json or "")
                        res = websocket.recv()
                res = await session.request(
                    method=self.method,
                    url=self.format_url_path(self.url, self.path),
                    json=self.json,
                    headers=self.headers,
                    params=self.query,
                    timeout=self.timeout
                )
                return func(response=res, *args, **kwargs)
            return wapperAsync
        else:
            def wapper(*args, **kwargs):
                self.update(kwargs=kwargs)
                if self.method == "ws" or self.method == "websocket":
                    with connect(self.url) as websocket:
                        websocket.send(self.body or self.json or "")
                        res = websocket.recv()
                res = httpx.request(
                    method=self.method,
                    url=self.format_url_path(self.url, self.path),
                    json=self.json,
                    headers=self.headers,
                    params=self.query,
                    timeout=self.timeout
                )
                return func(response=res, *args, **kwargs)
            return **wapper**

虽然我实在是不觉得把

events_api = API["qrcode"]["get_events"] 
params = {"qrcode_key": login_key} 
events = json.loads( 
   requests.get( 
       events_api["url"], 
       params=params, 
       cookies={"buvid3": str(uuid.uuid1()), "Domain": ".bilibili.com"}, 
   )

变成

@Request(method="GET", 
url=API["qrcode"]["get_events"]["url"], 
cookies={"buvid3": str(uuid.uuid1()), "Domain": ".bilibili.com"}
)

对于开发有多大意义,并没有怎么方便到...我大概不会去用装饰器

@Drelf2018 倒不如试试看怎么自动把 data 文件夹里面的 api 参数信息对应封装函数传入值,自动填入 request 函数的参来的实在

@z0z0r4
Copy link
Collaborator

z0z0r4 commented May 27, 2023

@z0z0r4 把这个merge了然后发release吧

感觉没法 merged,我是不是应该先发 release 火速把参数排序给处理了先 =~=

@Drelf2018
Copy link
Collaborator

@Drelf2018 倒不如试试看怎么自动把 data 文件夹里面的 api 参数信息对应封装函数传入值,自动填入 request 函数的参来的实在

#307

@z0z0r4
Copy link
Collaborator

z0z0r4 commented May 27, 2023

@z0z0r4 把这个merge了然后发release吧

感觉没法 merged,我是不是应该先发 release 火速把参数排序给处理了先 =~=

今晚发

@Drelf2018
Copy link
Collaborator

可以把这里的 __call__() 移动到现在的 Api 类下,然后合并。

@z0z0r4
Copy link
Collaborator

z0z0r4 commented Jul 1, 2023

可以把这里的 __call__() 移动到现在的 Api 类下,然后合并。

我期末考,你看看吧...

@z0z0r4 z0z0r4 deleted the branch Nemo2011:dev July 25, 2023 12:00
@z0z0r4 z0z0r4 closed this Jul 25, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants