🐎 第9章 集腋成裘:Plugins 生态
📄 一句话概括

装了两个包、各自都带一个叫 review 的东西,直接叫 review 会叫到谁? 实测四个调用:只有带命名空间的那两个落在你想要的包上,另外两个都落到了项目本地。

📝 本章和前面几章的关系

上一节讲装,这一节讲装多了之后的问题:同名。 这一节有真跑:四个调用逐个解析,看优先级怎么起作用。

一、命名空间长什么样

格式是「包名 加 冒号 加 组件名」。比如 corp-review:review。 包名来自清单里的 name 字段——所以那个字段不能随便改,改了所有调用都会失效。 这和编程语言里的模块前缀是一个道理:不是为了好看,是为了不撞。

设计包的时候就要想好前缀:组织名、领域名,都行,但要有意义。

二、真跑:四个调用逐个解析

演示里四个调用:review、corp-review:review、team-web:review、deploy-check。 带命名空间的两个,精确落到对应的包上。 不带的两个,都落到了项目本地的同名项上,而不是插件里的。

四个都解析成功了,但只有两个是你以为的那个——这就是命名冲突最麻烦的地方:它不报错。

三、优先级从哪儿来

显式命名空间最高:写了包名就直接去那个包找。 项目本地次高:当前项目里定义的。 用户级再次:你自己配的。

插件最低:装进来的包。 插件排最后这个设计是对的:装进来的东西不该悄悄盖掉你项目里的定义。 但它也意味着「装了却没生效」——因为项目里有个同名的。这个现象很难查。

四、命名冲突的三种表现

悄悄用错:不报错,用的是项目本地那份,你以为在用插件。 部分生效:命令能调,但里面引用的子智能体是另一个包的。 装不上:两个包声明了同一个包名,后装的那个可能覆盖前一个。

第三种最少见但最严重:它会让一个包「消失」,而你以为装好了。

五、一条实用规矩

给别人用的包,名字里带上组织或领域前缀:corp-、web-、data-。 这样撞名的概率低,而且看调用就能猜到是哪个包提供的。 自己临时试的包,用 tmp- 开头,用完就删——它不该留在别人的机器上。

调用别人的东西时,能写全名就写全名。省下的那几个字符,不值得一次排查。

六、动手 Demo:两个包都有 review,叫它的时候到底叫到谁

装了两个包、各自都带一个叫 review 的东西,直接叫 review 会发生什么? 这里把四个调用逐个解析,看优先级怎么起作用。

reg = {
    u"corp-review:review": u"插件 corp-review",
    u"review": u"项目本地(优先级最高)",
}

实跑输出:

--------------------------------------------------------------------
两个包都有 review,叫它的时候到底叫到谁
--------------------------------------------------------------------

  装了两个包、各自都带一个叫 review 的东西,直接叫 review 会发生什么?
  这就是命名空间要解决的问题:让「同名的东西」能共存,而且能被精确指到。

  ① 命名空间长什么样
  --------------------------------------------------------------------
  格式是「包名 加 冒号 加 组件名」。比如 corp-review:review。
  包名来自清单里的 name 字段——所以那个字段不能随便改,改了所有调用都会失效。
  这和编程语言里的模块前缀是一个道理:不是为了好看,是为了不撞。

  ② 真跑:四个调用逐个解析
  --------------------------------------------------------------------
| 你写的 | 怎么写的 | 实际叫到 |
|---|---|---|
| review | 只说名字 | 项目本地(优先级最高) |
| corp-review:review | 带命名空间 | 插件 corp-review |
| team-web:review | 带命名空间 | 插件 team-web |
| deploy-check | 项目里也有一份同名的 | 项目本地(同名时项目优先) |

  四个都解析成功了,但只有两个是你以为的那个——带命名空间的那两个。
  不带命名空间的两次,都落到了项目本地的同名项上,而不是插件里的。

  ③ 优先级从哪儿来
  --------------------------------------------------------------------
| 来源 | 优先级 | 说明 |
|---|:---:|---|
| 显式命名空间 | 最高 | 写了包名就直接去那个包找 |
| 项目本地 | 次高 | 当前项目里定义的 |
| 用户级 | 再次 | 你自己配的 |
| 插件 | 最低 | 装进来的包 |

  插件排在最后,这个设计是对的:装进来的东西不该悄悄盖掉你项目里的定义。
  但它也意味着「装了却没生效」——因为项目里有个同名的。这个现象很难查。

  ④ 命名冲突的三种表现
  --------------------------------------------------------------------
  - 悄悄用错:不报错,用的是项目本地那份,你以为在用插件。
  - 部分生效:命令能调,但里面引用的子智能体是另一个包的。
  - 装不上:两个包声明了同一个包名,后装的那个可能覆盖前一个。

  ⑤ 一条实用规矩
  --------------------------------------------------------------------
  给别人用的包,名字里带上组织或领域前缀:corp-、web-、data-。
  这样撞名的概率低,而且看调用就能猜到是哪个包提供的。
  自己临时试的包,用 tmp- 开头,用完就删——它不该留在别人的机器上。
  ! 优先级的层数与顺序以真实实现为准;这里演示的是「同名时谁赢」这件事。

①命名空间格式是「包名 加 冒号 加 组件名」,包名来自清单里那个 name 字段——所以它不能随便改。 ②四个调用都解析成功,但只有带命名空间的两个落在你想要的包上。另外两个落到了项目本地的同名项上。 ③优先级:显式命名空间最高,然后是项目本地、用户级,插件最低。 插件排最后是对的,但它意味着「装了却没生效」。 ④命名冲突的三种表现:悄悄用错、部分生效(命令在但引用的东西是别人的)、装不上(同包名互相覆盖)。

30 秒实验

  1. 把项目本地那份 review 删掉,重跑一遍。不带命名空间的调用会落到哪儿? 这说明「插件最低」这个规则的后果是什么?
  2. 让两个包都叫 corp-review,重跑一遍。会发生什么?这种事在实际里怎么发现?
  3. 把所有调用都改成带命名空间,重跑一遍。有什么代价?值得吗?

常见误区

  1. 包名不带前缀。撞名概率高,而且看调用猜不到来源。
  2. 以为装了就能用。项目本地有同名的,插件那份永远轮不到。
  3. 改已发布包的 name 字段。所有带命名空间的调用都会失效。
  4. 调试时只写简称。出问题时会怀疑到错的地方。
  5. 临时包用正式名字。它会留在别人机器上,和真包混淆。
自测三问(点开对答案) 1. 命名空间的格式是什么? —— 包名加冒号加组件名,比如 corp-review:review。包名来自清单的 name 字段。 2. 同名时谁赢? —— 显式命名空间最高,其次项目本地、用户级,插件最低。 3. 为什么「插件最低」会带来麻烦? —— 因为它意味着「装了却没生效」——项目里有同名的,插件那份永远用不上,而且不报错。

单个包讲完了,下一节把六个文件拼成一个能发给同事的团队包。

相关笔记