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

fix client.add_post RuntimeWarning #88

Closed
wants to merge 1 commit into from
Closed

fix client.add_post RuntimeWarning #88

wants to merge 1 commit into from

Conversation

adk23333
Copy link

  • return add_post.request(self._connector, self._core, tbs, fname, fid, tid, content) 缺少await关键字,已修复

return add_post.request(self._connector, self._core, tbs, fname, fid, tid, content)
缺少await关键字,已修复
@n0099
Copy link

n0099 commented Jan 25, 2023

如果某个async函数中没有任何await(因此这个async除了使其返回值类型变成awaitable[] monad以外毫无意义),那么可以人为增加await(如经典return await)来让async派上用场
然而这函数上面就有着两个await,所以阁下并不需要再人为添加return await来增加运行时awaitable monad实例的数量
https://github.com/Starry-OvO/aiotieba/blob/66a7ff7223ad850b586be2827442c761daa3171d/aiotieba/client/_client.py#L2060

实际上避免return await这种代码异味 除非函数返回值类型必须是一个异步monad而别的地方没啥可await的需要精细控制可能throw的exception应该来自哪个monad在其他采用了源自c#的async/await语法糖来实现CPS transform以允许编译器直接codegen无栈协程状态机样板代码的语言中都适用:
https://stackoverflow.com/questions/59260268/is-there-ever-a-reason-to-return-await-in-python-asyncio
https://eslint.org/docs/latest/rules/no-return-await
https://stackoverflow.com/questions/38708550/difference-between-return-await-promise-and-return-promise
https://stackoverflow.com/questions/19098143/why-use-async-and-return-await-when-you-can-return-taskt-directly

@adk23333 adk23333 closed this Jan 25, 2023
@adk23333 adk23333 reopened this Jan 25, 2023
@adk23333 adk23333 closed this Jan 25, 2023
@adk23333
Copy link
Author

谢谢,学到了,一开始是在教程示例的main函数里面发送回复会报错才找到这添加的😂

@lumina37
Copy link
Owner

emmm,确实是个遗漏,你要不要考虑往develop branch发个pr

@adk23333
Copy link
Author

emmm,确实是个遗漏,你要不要考虑往develop branch发个pr

已经发送了

@n0099
Copy link

n0099 commented Jan 25, 2023

谢谢,学到了,一开始是在教程示例的main函数里面发送回复会报错才找到这添加的😂

什么error?

emmm,确实是个遗漏,你要不要考虑往develop branch发个pr

阁下需要需要精细控制可能throw的exception应该来自哪个monad吗?

@n0099
Copy link

n0099 commented Jan 25, 2023

emmm,确实是个遗漏,你要不要考虑往develop branch发个pr

已经发送了

#90

@lumina37
Copy link
Owner

不在request里抛出的异常都被视为严重错误,比如MemoryError

@n0099
Copy link

n0099 commented Jan 26, 2023

经典把运行时异常甩给用户来自行处理,然后用户写代码时根本不知道可能会有这些那些类型的异常于是整个进程都exit with non-zero code
建议学习java checked @exception精神
或是四叶头子CS硕士PLT理论中级高手仏皇irol阁下 @kokoro-aya 最爱的error monad如Maybe[]

@lumina37
Copy link
Owner

lumina37 commented Jan 26, 2023

不知你能否理解在默认情况下抛给用户的错误只会有两种

  1. 服务端变更导致数据解析失败,鉴于贴吧没有关于api变更的通知,我觉得有必要在碰到任何这类异常时killall。譬如上次/pb/page的变更,导致大量用户因为语音信息被解析为指向新版本下载渠道的外链而被封
  2. MemoryErrorunchecked错误
    我希望用户侧的代码尽可能简洁,因此不会catch MemoryError并返回None

@n0099
Copy link

n0099 commented Jan 26, 2023

那不就是上次提到的静默掉几乎所有异常: #82 (comment)
然而

语音信息被解析为指向新版本下载渠道的外链

并不是解析失败,实际上从贴吧服务端来看反而是 https://en.wikipedia.org/wiki/Progressive_enhancement 服务端觉得aiotieba是一个远古版本客户端所以就返回可以被远古版本客户端正常解析的普通纯文本之升级提示

贴吧没有关于api变更的通知

所以阁下只能在针对贴吧接口的集成测试中发现api breaking changes,而不是在解析语音消息时做运行时assert看到有升级客户端的text就fail,因为任何8u都有权发表可以触发您的assert造成假阳性的文本

上次/pb/page的变更

实际上那次 #64 (comment) 更严重的是无法归因主题帖回复贴的author是谁,这不会导致许多删帖规则都爆炸?

@lumina37
Copy link
Owner

行,我比喻得有点问题,我应该用的比喻是【变更前,返回纯数字字符串,变更后,概率返回一个'-'】这种会导致解析错误

@lumina37
Copy link
Owner

你不会觉得靠用户输入就能killall吧

@n0099
Copy link

n0099 commented Jan 26, 2023

行,我比喻得有点问题,我应该用的比喻是【变更前,返回纯数字字符串,变更后,概率返回一个'-'】这种会导致解析错误

然而这仍然意味着阁下是在运行时不断assert/validate贴吧接口response的protobuf/json字段值

你不会觉得靠用户输入就能killall吧

这就是 https://en.wikipedia.org/wiki/Fail-fast

然而阁下的确无法通过对由用户输入组成的字段值进行运行时validate并将其变动归因为是贴吧接口而不是用户输入本身的变动

@kokoro-aya
Copy link

在tbm这种use case下,假阳性应该是更好的情形吧。毕竟用户可以自行更改对应的请求或者等待上游的更新,而不是说发一个东西,程序方面通过了,结果因为api更改了而被贴吧封号了。

我不了解python,我想如果穷尽贴吧的api做出一大堆对应的exception也是不切实际的想法。

@n0099
Copy link

n0099 commented Jan 29, 2023

在tbm这种use case下

您说的很对,但这里是aiotieba而不是tbm
而截止2023年1月,我还没有在tbm里写一大堆的运行时assert来试图validate贴吧api response中每一个字段值的取值范围(当然我也不知道starry神是否真的这么做了)


用户可以自行更改对应的请求

经典自己fork然后重头编译搭建环境


发一个东西,程序方面通过了,结果因为api更改了而被贴吧封号

starry神所举的例子

  1. 上次/pb/page的变更,导致大量用户因为语音信息被解析为指向新版本下载渠道的外链而被封

是指无辜8u人士所做的罪恶行径(如发帖)由于贴吧服务端暗改接口返回结果字段值取值范围导致其正好命中了用户(设置规则的用户如吧务)starry神的aiotieba/dog194所fork的贴吧管理器/鸡血神贴吧辅助工具中所预设的规则导致其被规则操作所影响到(如吧务删帖/封禁),也就是从规则的角度来看这反而是假阳性,是阁下所最痛恨的ai治国: https://www.v2ex.com/t/910423#r_12605435


穷尽贴吧的api做出一大堆对应的exception

连文档都拿不到怎么可能穷举,我看贴吧架构师自己都搞不清这堆了20年的微服务屎山里有多少取值范围


而有类型系统也只能narrow一点取值范围,比如规定一个数值只能是int而不会是字符串数字或js人最爱的ieee754,但很难限制这个int必定-10>x<100,因为这需要deptype或是ts那样类型系统as图灵完备语言 https://stackoverflow.com/questions/39494689/is-it-possible-to-restrict-number-to-a-certain-range https://github.com/ghoullier/awesome-template-literal-types 然而即便如此您也不可能要求所有与贴吧api交互的人所使用的语言都有着deptype或是都在写ts并懂的如何练习类型体操魔法(如土澳应用统计学带手子文科生cn神 @ControlNet
而在存储数据的层面如传统RDBMS中的字段类型同样类似编程语言的类型系统,即不可能只靠字段类型就约束某个存储的int必定-10>x<100(除非您想写TRIGGER在写入时进行validate,然而这跟在app层validate没啥区别)
所以最终还是回到在app层在运行时不断validate(不论是用户输入还是api response)值以避免数据污染,当然在这方面早已有语言无关的interop标准之json-schema https://json-schema.org/understanding-json-schema/reference/numeric.html

然而对于验证api response这种场景,我进一步指出

无法通过对由用户输入组成的字段值进行运行时validate并将其变动归因为是贴吧接口而不是用户输入本身的变动

就好比当您规定某值是NULL就代表他不存在,然而用户也有可能提供NULL值,于是您无法只通过判断isnull就assert其一定是值不存在而不是用户输入了NULL(所以js特色之同时有着nullundefined反而是优点,其给用户带来了更多的语义,尽管这个特色无法interop因为其他语言不会区分两个null出来),这也就是此前我指出在mysql中存储8个NULL只要1byte空间而存储8个为0的int324*8=64byte空间twitter-monitor作者 @BANKA2017 进一步指出如果int值本身就是NULLABLE就无法利用这个空间优化


我的建议是 n0099/open-tbm#32 (comment)

提请四叶信安底层壬上壬上海贵族 FSF EFF 精神会员杨博文阁下 @yangbowen 立即前往v2ex回复四叶头子CS硕士PLT理论中级高手仏皇irol阁下所提出的奇思妙想如果语言能够提供足够的表达性,是否可以不需要反射和 AOP 的概念?v2ex.com/t/910403

@ControlNet
Copy link

而有类型系统也只能narrow一点取值范围,比如规定一个数值只能是int而不会是字符串数字或js人最爱的ieee754,但很难限制这个int必定-10>x<100,因为这需要deptype或是ts那样类型系统as图灵完备语言 https://stackoverflow.com/questions/39494689/is-it-possible-to-restrict-number-to-a-certain-range https://github.com/ghoullier/awesome-template-literal-types

这个挺有趣的,但是只能拿来赋值用感觉,后面接表达式就不行了,而且也只支持整数

@yangbowen
Copy link

而有类型系统也只能narrow一点取值范围,比如规定一个数值只能是int而不会是字符串数字或js人最爱的ieee754,但很难限制这个int必定-10>x<100,因为这需要deptype或是ts那样类型系统as图灵完备语言 https://stackoverflow.com/questions/39494689/is-it-possible-to-restrict-number-to-a-certain-range https://github.com/ghoullier/awesome-template-literal-types

这个挺有趣的,但是只能拿来赋值用感觉,后面接表达式就不行了,而且也只支持整数

它既然是图灵完备那当然可以表达式的吧……?

@ControlNet
Copy link

而有类型系统也只能narrow一点取值范围,比如规定一个数值只能是int而不会是字符串数字或js人最爱的ieee754,但很难限制这个int必定-10>x<100,因为这需要deptype或是ts那样类型系统as图灵完备语言 https://stackoverflow.com/questions/39494689/is-it-possible-to-restrict-number-to-a-certain-range https://github.com/ghoullier/awesome-template-literal-types

这个挺有趣的,但是只能拿来赋值用感觉,后面接表达式就不行了,而且也只支持整数

它既然是图灵完备那当然可以表达式的吧……?

image

@n0099
Copy link

n0099 commented Jan 29, 2023

因为30+10的类型是number
因为number类型的+运算符的返回值是number
因为3010都是number所以使用number类型的+运算符而不是string类型的(也就是concat)

您需要手动把30+10的返回值narrow到IntRange<30, 30>ts playground
image

然后您会发现很容易类型逃逸出去
image
这是因为任何number都可以as强转为任何类型参数值的intrange<,>之中

@n0099
Copy link

n0099 commented Jan 29, 2023

阁下需要从ts类型系统的视角来看:ts不知道什么是jsexpr之30+10,他只能从js代码ast中推导出30是number,而10也是number
因此这是个number+number,所以ts知道这里的+运算符肯定是指数值加而不是字符串连接,那么ts就可以(或者说必须,因为ts不可能把整个number+number作为一种类型信息来使用,即类型expr本身不是类型,只有对expr进行eval后才能获得类型,也可以将这个+运算符视作是(type, type) => type,所以+运算符构成的expr相当于一个尚未eval的函数,ts不可能把这个函数(类型expr)当做值(类型)来使用,而只能将函数返回值作为值使用,这也从侧面解释了为什么ts不支持HKT)把整个expr的类型变成单个number(因为数值加运算符的返回值是number)

即便您强迫ts直接把30和10视作literal type而不是退化到number,ts在看到+运算符后也还是只能直接将这个运算符的返回值类型作为expr的类型:
image
如果您希望ts能够知道30+10=40,那就意味着ts层面必须能够eval任意js表达式(即便您在这里只需要一个简单的number+运算符)而此前已经说明了ts类型层面与js代码层面的唯一交互方式和通信媒介是类型信息(ts类型推导从js代码ast中获得类型信息),那您又如何将js expr30+10=40的结果告知ts类型系统呢?

然而这一切仍然是将类型运算视作lambda演算(即对没有body什么也做不了的纯函数之间的运算)的思维方式
回顾经典之奥利金德可计算性数理逻辑带手子pixeval作者网哲dc神 @dylech30th 的部落格中的comment:https://sora.ink/archives/1574?replytocom=775#respond

“不如说在类型系统的层面看来任何函数本就只有类型信息而不存在body中实现的逻辑”——这一点并不蕴含了任何类型系统都享有Parametricity,类似forall X. X → X这种类型之所以能直接写出他的实现是因为类型X是被抹除的,本身不蕴含任何其他有价值的信息

又如四叶信安底层壬上壬上海贵族 FSF EFF 精神会员杨博文阁下此前在四叶TG群称其将cpp类型系统理解为集合运算,而我前年使用ts过程中也是如此理解ts的奇妙深刻高度自由的类型表达力的
这两种思维方式很明显都与理论层面是有着巨大出入的(因此奥利金德类型论范畴论带手子dc神仏国CS硕士PLT理论中级高手irol阁下都对此不置可否),也无法区分dc神此前发表的科普论文哲学,逻辑,与数学——从罗素悖论谈起中所提出的类型系统的8种写法:
https://en.wikipedia.org/wiki/Lambda_cube
image

Lambda立方体,它的八个顶点对应八种不同的类型系统,左下角的起点对应简单类型Lambda演算,右上角的顶点对应构造演算,其x轴对应依赖类型,y轴对应多态,z轴对应高阶类型

@n0099
Copy link

n0099 commented Jan 29, 2023

而有类型系统也只能narrow一点取值范围,比如规定一个数值只能是int而不会是字符串数字或js人最爱的ieee754,但很难限制这个int必定-10>x<100,因为这需要deptype或是ts那样类型系统as图灵完备语言 stackoverflow.com/questions/39494689/is-it-possible-to-restrict-number-to-a-certain-range ghoullier/awesome-template-literal-types

这个挺有趣的,但是只能拿来赋值用感觉,后面接表达式就不行了,而且也只支持整数

它既然是图灵完备那当然可以表达式的吧……?

至于我声称ts类型系统是图灵完备语言是从最经典的两个论点来看的:

图灵完备的条件/语言 条件控制 没有终止条件的循环
其他c-style语言 逻辑流语句 递归函数
typescript的类型系统 conditional type recursive type
ANSI SQL:99中定义的语句
不包括存储结构那样的
命令式IF FOR语句
WHERE clause WITH RECURSIVECTE

microsoft/TypeScript#14833
https://stackoverflow.com/questions/900055/is-sql-or-even-tsql-turing-complete
https://news.ycombinator.com/item?id=22374794
https://wiki.postgresql.org/wiki/Mandelbrot_set
https://observablehq.com/@pallada-92/sql-3d-engine

然而在现实中为了更好的满足需求我们可能反而需要的是图灵不完备语言,如同早已被irol阁下批倒批臭的lowcode https://www.v2ex.com/t/911081#r_12614035: https://news.ycombinator.com/item?id=10567408

@dylech30th
Copy link

我求求你了不要一直at我了我看github邮件猛地一惊还以为有啥事或者issue或者pr了结果点进来一看又是你个n9在at我 @n0099

@yangbowen
Copy link

我求求你了不要一直at我了我看github邮件猛地一惊还以为有啥事或者issue或者pr了结果点进来一看又是你个n9在at我 @n0099

我也早就让他不要无端mention我。

@n0099
Copy link

n0099 commented Jan 29, 2023

只支持整数

也是很明显的
image
首先类型Enumerate<N>的目的就是生成从0开始的一堆正整数直到N并作为union literal types表达

ts事实核查

  1. 您可以利用extends这个subtyping检查运算符来模拟数值相同运算符(如同bash的test[10 -eq 0]
    image
  2. 您可以在ts中使用js的array spread ... operator以进行类型层面上的array merge操作 https://basarat.gitbook.io/typescript/future-javascript/spread-operator
    image
  3. 您可以像js那样使用[]运算符获得某个array类型中特定index成员的类型: https://www.typescriptlang.org/docs/handbook/2/indexed-access-types.html
    image
    而如果index是number意味着获取所有成员的集合:
    image

因此结合三大事实,您可以写出等效的js递归:ts playground
image

TL;DR

所以为什么类型Enumerate<N>只能输出一堆整数类型的union?
因为js array的length都是0开始的正整数,也就是说js中不存在具有1.5(或任何同为js的number类型所能表达的IEEE754小数)个成员的array

而最后的IntRange<F extends number, T extends number> = Exclude<Enumerate<T>, Enumerate<F>>不过是将从0到T的正整数集合减去从0到F的正整数集合:
image

然而ts的集合减法运算符之Exclude<>是如何工作的? https://www.typescriptlang.org/docs/handbook/utility-types.html#excludeuniontype-excludedmembers
image
乍一看只是个<T, U> = T extends U ? never : T,相当于js的(t, u) => t === u ? null : t,然而当输入类型是union types时(大多数时候给Exclude输入的类型参数都是),由于conditional type具有分配律: https://www.typescriptlang.org/docs/handbook/2/conditional-types.html#distributive-conditional-types
image
导致Exclude会对于每个T和U类型集合的成员都执行一次extends判断,获得所有判断的返回值never | T(要么never要么T),再从中剔除never类型,就得到了只剩下T类型,并且每个T类型都extends U类型集合中的某个成员类型的union type
如果您复制粘贴一个Exclude出来然后把never改成null就更能看出来这一步:
image

union中剔除neverimage
so人 https://stackoverflow.com/questions/64230626/why-is-the-type-never-meaningless-in-union-types 早已道明真相:ts文档 https://www.typescriptlang.org/docs/handbook/basic-types.html#never 指出never是a subtype of, and assignable to, every type; however, no type is a subtype of, or assignable to, never (except never itself). Even any isn’t assignable to never.
因此如果应用此前提到的将类型运算视作集合运算思维方式:
never就是,而&就是set intersect|就是set union(所以他们在ts中直接就叫intersect/union type
而众所周知任何集合跟 union的结果还是任何集合本身
或者从群论角度来看:
在并集运算中是单位元,因为 $\emptyset \cup \{1,2\} = \{1,2\}$ $\{3,4\} \cup \emptyset = \{3,4\}$
而并集运算又具有交换律: $(\{1,2\}|\{3,4\})|\{5,6\} = \{1,2,3,4,5,6\}$ $\{1,2\}|(\{3,4\}|\{5,6\}) = \{1,2,3,4,5,6\}$
所以并集运算是一个单位半群
这下单子(monad)是自函子范畴上的幺半群(monoid)了

与此同时您只需要将Exclude完全倒过来,他跟&就是一样的:
image

@dylech30th
Copy link

你都从群论的角度考虑了,为什么不一步到位用 lattice 来描述 subtyping relation 呢?subtyping relation对应了一个 bounded lattice,那never就是最小元素 $\bot$any就是最大元素 $\top$。所以自然而然的, $A\lor B = A \iff B=\bot$,以及 $A\lor B = B\iff A =\bot$

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants