目录

17 · getters.py:从指令流提取账户、币种与时间信息

核对基线 · 范围 · 依赖

核对基线:beancount 仓库 commit 97472138(2026-08-22)。路径相对仓库根目录,引用格式 文件:起-止行 (名称),行号已逐条核对。 本篇范围beancount/core/getters.py(382 行)与 beancount/core/getters_test.py(270 行)。 上游依赖beancount/core/data.py(运行时只导入 CloseCommodityOpenTransaction 四个指令类,getters.py:15-18)、beancount/core/account.pysplit)。其余类型名放在 TYPE_CHECKING 块里(:20-34)。 下游使用者:15 个非测试文件导入本模块;beancount/api.py:60-61 只把 get_account_open_closeget_accounts 两个名字重新导出。

1. 模块解决什么问题

账本加载完是一条按日期排好序的指令列表(Directives = list[Directive]data.py:504)。很多操作要的不是逐条指令,而是从整条流里横切出来的汇总视图:这本账出现过哪些账户、每个账户第一次和最后一次被用到是哪天、哪些账户有 Open/Close 指令、有哪些标签链接收款人、声明过哪些 Commodity、账本跨了哪几年。getters.py 把这些一次性遍历写成十几个彼此独立的顶层函数:多数以 entries 为输入,另有 get_leveln_parent_accountsget_dict_accounts 接收账户名集合,get_values_meta 接收名字到指令的映射(:228-263,356-382)。共同点是不改输入、不留状态;在标准类型契约内不返回 Beancount 错误列表,但不合契约的输入会直接抛出 Python 异常——get_active_years 对非连续重复的年份 assert,未知指令类型走到分发处触发 AttributeError:52,70),get_min_max_dates 传入生成器而非序列时 reversed() 触发 TypeError:289)。

模块 docstring 把自己定位成 "Getter functions that operate on lists of entries to return various lists of things that they reference"(getters.py:1-3)。

2. 结构一览

名称 位置 作用
class GetAccounts getters.py:37-116 账户提取器,按指令类名分发到同名方法
GetAccounts.get_accounts_use_map :40-57 遍历全流,产出首次/末次使用日期两张表
GetAccounts.get_entry_accounts :59-71 单条指令的账户集合
GetAccounts.Transaction / .Pad / ._one / ._zero :73-112 四个提取实现
分发表 Open = Close = ... = _one :114-116 十种类型经别名绑到 _one/_zero,加 Transaction、Pad 共覆盖 12 种
_GetAccounts = GetAccounts() :119-120 全局共享实例
get_accounts_use_map / get_accounts / get_entry_accounts :123-158 三个顶层包装
get_account_components :161-174 所有账户名切分后的组件去重排序
get_all_tags / get_all_payees / get_all_links :177-225 只扫 Transaction,返回排序后的 list
get_leveln_parent_accounts :228-245 横切账户树的第 N 层
get_dict_accounts :248-263,标签 :266 把账户名列表折成嵌套 OrderedDict
get_min_max_dates :269-295 取首尾指令的日期
get_active_years :298-314 生成器,产出有指令的年份
get_account_open_close :317-342 账户名到 [open, close] 的映射
get_commodity_directives :345-353 币种名到 Commodity 指令
get_values_meta :356-382 从一张"名字→指令"表里批量取元数据

3. 账户提取:GetAccounts

3.1 按类名 getattr 分发

for entry in entries:
    method = getattr(self, entry.__class__.__name__)
    for account_ in method(entry):

getters.py:51-53。分发不是 isinstance 链,也不是显式字典,而是用指令类的 __name__ 去类实例上取同名属性。可取的属性由类体末尾两行赋值造出:

# Associate all the possible directives with their respective handlers.
Open = Close = Balance = Note = Document = _one
Commodity = Event = Query = Price = Custom = _zero

getters.py:114-116。加上单独定义的 Transaction:73-82)和 Pad:84-92),十种经别名绑到 _one/_zero,加 Transaction、Pad 共四个实现,恰好覆盖 data.ALL_DIRECTIVES 里的 12 个类型(data.py:452-465)。四个实现的语义是:Transaction 逐条 posting 产出账户;Pad 返回 (entry.account, entry.source_account) 两个账户;_one 返回单元素元组 (entry.account,)_zero 返回空元组。四者返回值都是可迭代对象,调用方一律 for ... in method(entry),不区分生成器与元组。

方法的第一个参数名写作 _ 而不是 self:73,84,94,104),表示不依赖实例状态;实例 _GetAccounts 建成模块级单例(:119-120),三个顶层包装函数转发到它(:123-158)。

这套写法带一条作者注释:

Note: This should get replaced by a method on each directive eventually,
that would be the clean way to do this.

getters.py:62-63get_entry_accounts 方法的 docstring),同一段话在顶层包装 get_entry_accounts 的 docstring 里重复了一遍(:150-151)。作者把"账户提取表"集中在一个外部类里标记为临时方案,认为归位的做法是让每个指令类自己提供方法。这条注释自 2014-06-21 commit ba76130a 引入 get_entry_accounts 时就写在那里,至今未变。

分发靠属性名字符串,代价是没有类型检查兜底:传入 ALL_DIRECTIVES 之外的对象(例如 data.TxnPosting)时 getattrAttributeError,而不是被忽略或报错误对象。getters_test.py:47-50 (test_methods_coverage)hasattr(dispatcher, klass.__name__) 遍历 data.ALL_DIRECTIVES 锁定"每种指令都有对应方法",是新增指令类型时的唯一防线。

3.2 三个账户提取入口

get_accounts_use_map 是基础实现,边遍历边填两张 dict:

for entry in entries:
    method = getattr(self, entry.__class__.__name__)
    for account_ in method(entry):
        if account_ not in accounts_first:
            accounts_first[account_] = entry.date
        accounts_last[account_] = entry.date

getters.py:51-56accounts_first 只在键不存在时写入,accounts_last 每次覆盖——"第一次"和"最后一次"都由遍历顺序决定,函数本身不比较日期。docstring(:41-48)没有声明输入需已排序;正确性完全依赖调用方传入的是加载器排好序的流。

get_accountsaccounts_last 取键成集合(:143-144),docstring 注明两张表的键相同(:129-130)。get_entry_accountsset(method(entry)):70-71),每次返回新集合,调用方可以直接改;interpolate.py:337-339 就是拿到后 update(additional_accounts)

Open 和 Close 都走 _one,所以一个只被 open 声明、从未记过账的账户仍会出现在 get_accounts 的结果里,accounts_last 也会被 Close 指令的日期刷新。getters_test.py:52-74 锁定了这两点:五个账户的 accounts_first 全是 open 那天 2012-02-01,Assets:US:Cashaccounts_last 是 close 那天 2014-02-01,而没有 close 的 Expenses:Grocery 停在最后一次交易 2012-05-18。

3.3 get_account_open_close:重复指令只留最早的一条

open_close_map = defaultdict(lambda: [None, None])
for entry in entries:
    if not isinstance(entry, (Open, Close)):
        continue
    open_close = open_close_map[entry.account]
    index = 0 if isinstance(entry, Open) else 1
    previous_entry = open_close[index]
    if previous_entry is not None:
        if previous_entry.date <= entry.date:
            entry = previous_entry
    open_close[index] = entry

return dict(open_close_map)

getters.py:330-342。docstring 明说规则:"If an open or close entry happens to be duplicated, accept the earliest entry (chronologically)"(:320-321)。写法是保留槽位里已有的那条,仅当新条目更早时才替换;日期相等时 <= 让先出现的那条胜出。重复的 Open/Close 本身由 ops/validation.py 报成错误,这里只保证下游拿到的映射是确定的一条。这段防御来自 2014-06-21 commit ba76130a,与之前的写法(直接 open_closes_map[entry.account][index] = entry,后者覆盖前者)相反。

值是长度 2 的可变 list 而非元组,构造期需要按下标改写。返回时套一层 dict():342):2017-05-08 commit 4489fcb2 做的改动,CHANGES 记为 "Made the open/close map return by the getters not a defaultdict by default, so that accidentally looking up elements won't mutate it."(CHANGES:1360-1362)。同一个 commit 把 plugins/leafonly.py 改成 try/except KeyError(现为 leafonly.py:48-51),因为查一个不存在的账户不再静默插入 [None, None]

未出现在任何 Open/Close 指令里的账户根本不进这张表,所以 account not in open_close_map 表示"从未声明过",scripts/doctor.py:530-531 用这个条件生成缺失的 Open 指令。

3.4 对"输入已排序"的两处假设

get_min_max_dates 不做 min/max,直接取首尾:

for entry in entries:
    if types and not isinstance(entry, types):
        continue
    date_first = entry.date
    break

for entry in reversed(entries):
    ...

getters.py:283-293。两个循环各自 break,成本是 O(1)(带 types 过滤时是 O(k)),前提是 entries 已按日期升序。reversed() 还要求入参是序列,传生成器抛 TypeError。空列表或 types 过滤后无匹配时两个变量保持初值 None,返回 (None, None):281,295)。

get_active_years 把同样的假设写成断言:

seen = set()
prev_year = None
for entry in entries:
    year = entry.date.year
    if year != prev_year:
        prev_year = year
        assert year not in seen
        seen.add(year)
        yield year

getters.py:306-314。只在年份切换时产出,seen 集合的作用是让同一年份在被其他年份隔开后再次出现时立即 AssertionError——它不能验证输入整体是否按日期或年份升序:降序输入(如 2020、2019)不触发断言,正常产出 [2020, 2019];只有 2020、2019、2020 这种非连续重复的年份才会被拦下。这三行是 2013-07-05 commit 3c568161 加进去的,比 get_min_max_dates 的首尾取法更早地把"已排序"这个前提显式化。函数是生成器,断言在消费时才触发;python -O 下断言被剥离,非连续重复的年份会被重复产出而不报错。

两处对排序的依赖能够成立,靠的是加载器的保障而非 getter 自身校验:标准加载路径在 booking 前对全部指令排序,并在每个插件执行完后重新排序,防止插件打乱顺序(loader.py:596-602,738-740)。

4. 其余提取函数

函数 位置 输入 输出与要点
get_all_tags :177-191 entries 只看 Transactionif entry.tags:update,返回 sorted(set)
get_all_links :211-225 entries 同上,字段换成 links
get_all_payees :194-208 entries 无条件 add(entry.payee),末尾 all_payees.discard(None) 再排序
get_commodity_directives :345-353 entries 字典推导,只收 Commodity 指令;同名重复时后者覆盖前者
get_values_meta :356-382 名字→指令的 dict meta_keys 取元数据;单键返回值本身,多键返回元组;指令为 None 或键缺失时用 default
get_account_components :161-174 entries get_accountsaccount.split,组件去重排序
get_leveln_parent_accounts :228-245 账户名 list 统计第 level 层组件出现次数,只留 count > nrepeats
get_dict_accounts :248-263 账户名可迭代 嵌套 OrderedDict

三个 get_all_* 都跳过非 Transaction 指令(:187-188,204-205,221-222),所以只挂在 Note/Document 上的 tags/links(data.py:309-310,423-424)不会被收进来。get_all_payees 与另外两个不同,先把 None 一起塞进集合再 discard,省掉循环里的判空;返回值带 # type: ignore[arg-type] 注释(:208),因为静态类型看不出 discard 之后集合里不再有 None。三者返回排序好的 list(docstring 仍写 set,见第 6 节):get_all_tagsget_all_payees 从返回 set 改为排序 list 来自 2015-01-04 commit b2e4e387get_all_links 由 2018-03-27 commit 4e5b638f 新增,实现之初就是 sorted(all_links),不存在"从 set 改造"的历史。

4.1 get_dict_accounts:既是叶子又是前缀的账户

leveldict = OrderedDict()
for account_name in account_names:
    nested_dict = leveldict
    for component in account.split(account_name):
        nested_dict = nested_dict.setdefault(component, OrderedDict())
    nested_dict[get_dict_accounts.ACCOUNT_LABEL] = True
return leveldict

get_dict_accounts.ACCOUNT_LABEL = "__root__"

getters.py:257-266。每个账户名逐层 setdefault 下钻,走到底后在最内层字典里插一个 "__root__": True 标记。这个标记回答的问题是:"这个节点本身是一个账户名,还是仅仅是别的账户名的中间层?"Expenses:GroceryExpenses:Grocery:Bean 同时存在时,Grocery 节点的字典里既有 "__root__": True 又有子键 "Bean"——它既是叶子又是前缀。标记名不会与真实组件名冲突:账户组件的正则要求首字符是大写字母或数字(account.py:37-38),下划线开头的名字进不了合法账户名。

标记挂在函数对象上而不是模块级常量(:266),带 # type: ignore[attr-defined];调用方通过 getters.get_dict_accounts.ACCOUNT_LABEL 取用,getters_test.py:142 就是这么写的。

OrderedDict 而不是普通 dict,意味着键的顺序参与相等比较,而顺序由输入的账户名顺序决定:__root__ 与子键谁先出现,取决于 Expenses:GroceryExpenses:Grocery:Bean 哪个先被处理。getters_test.py:170 那行 ("Bean", root), # Wrong order here 标记的正是这一点(详见第 7 节)。

5. 设计决策与理由

决策 理由 证据
账户提取用类名 getattr 分发,而非 isinstance 分发表就是类体末尾两行赋值,新增指令类型时只需补一行;提取逻辑集中在一处 getters.py:52,70,114-116
分发器实例化成模块单例 方法不依赖实例状态(首参写作 _),无需每次调用新建对象 getters.py:73,119-120
作者标注分发表是临时方案 归位的做法是每个指令类自带方法;集中表与 data.py 的类定义分离,易漏 getters.py:62-63,150-151
use_map 两张表靠遍历顺序而非比较日期 输入已由加载器排序,一次遍历即可,无需 min/max getters.py:54-56
重复 Open/Close 保留最早一条 结果确定、可复现;重复本身的报错由 validation 负责 getters.py:320-321,336-339
open/close 映射返回普通 dict defaultdict 被误查会插入新键,污染映射 getters.py:342CHANGES:1360-1362
get_min_max_dates 取首尾 已排序前提下 O(1);带 types 过滤时跳过不匹配项继续找 getters.py:283-293
get_active_yearsseen 集合表达"每年至多一个连续区段" 非连续重复年份立即失败;不能验证输入整体是否升序 getters.py:312
get_all_* 返回排序 list 输出可复现,可直接用于渲染 getters.py:191,208,225;tags/payees 见 b2e4e387,links 见 4e5b638f
get_dict_accounts"__root__" 标记账户节点 中间节点可能同时是账户名;标记名与合法组件名不可能冲突 getters.py:262,266account.py:37-38
get_commodity_directives 只收 Commodity 指令 调用方只关心显式声明挂的元数据,不需要从 posting 里推断币种 commit bd6cad6f 的提交信息

6. 行为细节与边界

现象 后果 证据
分发靠 getattr(self, 类名) 传入 ALL_DIRECTIVES 之外的对象抛 AttributeError;新增指令类型忘记登记时同样如此 getters.py:52,70getters_test.py:47-50
类体内 Open = Close = ... = _one 遮蔽了模块导入的同名类 仅在 GetAccounts 的类命名空间内;模块级 Open/Close/Transaction/Commodity 不受影响,get_account_open_closeget_all_tags 用的仍是真实类 getters.py:15-18,114-116,332,187
Custom_zero Custom 指令的 values 里可以出现账户名(account.py:44-46TYPE 哑对象就是为在 custom 值里区分字符串与账户名而设),这些账户不会被 get_accounts 收集;但 realization.pypostings_by_account 会识别 dtype == account.TYPE 的 Custom 值并把该指令挂到对应账户下,两套账户发现规则并不一致 getters.py:116data.py:445-448account.py:44-46realization.py:309-313
_zero 的 docstring 与 get_all_*Returns 描述与实际返回类型不符 【文档漂移】_zero docstring 写 "An empty list",实际 return () 是空元组;get_all_tags/get_all_payees/get_all_linksReturns 写 "A set of ... strings",实际返回排序后的 list getters.py:104-112:182-183,199-200,216-217
Open/Close 计入账户的"使用日期" 只声明未记账的账户仍出现在 get_accounts 里;accounts_last 会被 close 日期刷新 getters.py:115getters_test.py:65-73
get_accounts_use_map 的正确性依赖输入已排序 docstring 未声明该前提;乱序输入不报错,直接得到错的首末日期 getters.py:41-48,54-56
get_min_max_dates 对生成器不可用 reversed(entries) 要求序列,生成器抛 TypeError getters.py:289
get_min_max_dates 空输入或过滤后无匹配 返回 (None, None),与"账本只有一条指令"时返回同一日期两次的形状一致 getters.py:281,295
get_active_yearsassertpython -O 下失效 非连续重复的年份会被重复产出,而不是提前失败 getters.py:312
get_active_years 是生成器 断言在消费时才触发,不是调用时 getters.py:298,314
get_account_open_close 的值是可变 list 拿到的 [open, close] 可被调用方改写,且会反映到映射里;测试、balance.pysummarize.py 解包为二元组;leafonly.pycheck_average_cost.py[0]doctor.py 只判断键存在 getters.py:330,340getters_test.py:207ops/balance.py:110ops/summarize.py:721-722plugins/leafonly.py:47-51plugins/check_average_cost.py:62-69scripts/doctor.py:527-531
同一币种多条 Commodity 指令 字典推导里后者覆盖前者,与 open/close 保留最早的规则相反;但重复 Commodity 本身会被 validate_duplicate_commodities 判为错误,"后者覆盖前者"只是错误输入仍被继续处理时的局部结果,不是受支持的语义 getters.py:353ops/validation.py:160-191CHANGES:4096-4099
get_values_metadefault 同时兼两种缺失 指令为 None 与元数据键不存在,返回同一个默认值,调用方无法区分;不传 meta_keys 时也不报错,每个名字返回空元组 () getters.py:374-381
get_leveln_parent_accountsset(account_names) 去重 nrepeats 计的是"有多少个不同账户在该层用了这个组件",不是出现总次数;level 不校验非负,负数按 Python 负索引从末级取组件,越过负边界抛 IndexError getters.py:240-244
get_min_max_datestypes=() 空元组是假值,if types and ... 判断为假,等同于不启用过滤,不会得到"无类型匹配"的 (None, None) getters.py:283-292
get_dict_accounts 的键顺序取决于输入顺序 OrderedDict 相等比较看顺序,同一批账户名换个顺序喂进去得到不相等的结果 getters.py:257-263getters_test.py:170,182-188
9 个函数在当前仓库内无非测试调用点 get_account_componentsget_all_tagsget_all_payeesget_all_linksget_leveln_parent_accountsget_dict_accountsget_min_max_datesget_active_yearsget_values_meta 只被 getters_test.py 调用;v2 的 web 界面与 reports 模块在 v3 中已移除 全仓库 grep;commit 5f8aaddebd6cad6f 的 stat

7. 测试锁定了什么

getters_test.py 一个类 TestGetters:46),17 个 test_ 方法,大部分共用同一段 TEST_INPUT:12-43):5 条 open、2 条 commodity、3 笔交易、2 条 close。

测试 行号 锁定的行为
test_methods_coverage 47-50 data.ALL_DIRECTIVES 里每个类名都能在 GetAccounts 上取到属性
test_get_accounts_use_map 52-74 两张表的完整内容:首次日期全是 open 日;末次日期区分有无 close
test_get_accounts 76-88 返回 5 个账户的 set
test_get_entry_accounts 90-97 三腿交易返回 3 个账户(含自动补全的那腿)
test_get_all_tags 99-102 ["books", "dinner"],已排序去重
test_get_all_payees 104-107 只有两个 payee;无 payee 的交易不产出 None
test_get_all_links 109-112 两笔交易共用的 link 只出现一次
test_get_leveln_parent_accounts 114-130 level 0/1/2 三层横切的结果
test_get_dict_accounts 132-188 见下
test_get_min_max_dates 190-194 2012-02-01 与 2014-02-01
test_get_active_years 196-199 [2012, 2013, 2014]
test_get_account_open_close 201-214 表长 5;逐个账户断言 open/close 是否为 None
test_get_account_open_close__duplicates 216-229 两条 open 两条 close 时,取 01-01 的 open 与 01-28 的 close;用 expect_errors=True 接受 validation 报的重复错误
test_get_account_components 231-244 8 个组件,已排序
test_get_commodity_directives 246-252 键为 {"HOOL", "PIPA"},值都是 Commodity
test_get_values_meta__single 254-258 单键返回值本身,不套元组
test_get_values_meta__multi 260-266 多键返回元组,缺失的 ticker 为默认值 None

test_get_dict_accounts:132-188)的形态特殊。输入里 Expenses:Grocery 排在 Expenses:Grocery:Bean 前面,所以实际结果中 Grocery 节点是 {"__root__": True, "Bean": {...}};测试把期望值写成相反的顺序并标注 # Wrong order here:170),断言用的是 assertNotEqual:182),演示的是"OrderedDict 的相等比较看顺序"。随后 :183-188account_dict["Expenses"]["Grocery"] 改成正确顺序,但函数到此结束,没有再做 assertEqual——修正后的期望值没有被任何断言使用。这个形态从 2018-03-11 commit 6e97fe97 引入起就是如此:该 commit 把原先针对普通 dict 的 assertEqual 改成了 OrderedDict 版本的 assertNotEqual

getters_test.py 未直接覆盖的行为:GetAccountsPad 指令的提取(TEST_INPUT 里没有 Pad);get_min_max_datestypes 参数与空输入;get_active_yearsassert 触发路径;get_leveln_parent_accountsnrepeats > 0 与负数 levelget_values_meta 传入值为 None 的表以及不传 meta_keys;分发到不存在方法时的 AttributeErrortest_methods_coverage:47-50)只验证 GetAccounts 上存在同名属性,不验证指令是否分配到正确处理器。

全仓库层面:TestValidateActiveAccountsvalidation_test.py:135-201)经 get_entry_accounts 断言含 Note、Pad、关闭后 Document 的错误;TestValidateDuplicateCommodities:117-132)断言重复 Commodity 错误;但均未锁定 getters.py 内部语义——Pad 双账户返回、各指令到处理器映射、get_commodity_directives 的"后者覆盖前者",全仓库无测试直接断言。

8. 演变史

主线 2017-04-30 移除 src/python/ 前缀(CHANGES:1364-1369),但历史分支提交仍可能显示旧路径:晚于该日期的 4e5b638f(2018-03-27)diff 仍是 src/python/beancount/core/getters.py,不可仅按日期推断路径。

日期 提交 / 记录 变化
2013-07-05 3c568161 get_active_years 加入 seen 集合与 assert year not in seen
2014-06-21 ba76130a GetAccounts.__call__ 改名 get_accounts;新增 get_entry_accounts 与那条 "should get replaced by a method on each directive" 注释;引入全局单例;get_account_open_close 加入"重复取最早"规则
2014-08-31 ade7e62a get_accounts 扩展出 use_map(首次/末次使用日期),为 auto_accounts 插件的自动补 Open 服务
2015-01-04 b2e4e387 get_all_tagsget_all_payees 从返回 set 改为返回排序后的 list
2015-01-17 96e6bb13 新增 get_commodity_mapget_values_meta;同时加入 test_methods_coverage
2015-01-18 CHANGES:4086-4094 Commodity 指令正式加入,配套函数为 get_commodity_map
2015-11-28 a3c06172 删除 get_commodity_map 的一个未使用参数
2016-02-28 376a3ccc 新增 Custom 指令类型,分发表加上 Custom = ... = _zero
2017-05-08 4489fcb2 get_account_open_close 返回 dict(...) 而非 defaultdict;连带修改 leafonly.py
2018-03-10 b66f8bee 新增 get_dict_accounts(外部贡献)
2018-03-11 6e97fe97 该函数的测试改用 OrderedDict 并加入 "wrong order" 演示
2018-03-27 4e5b638f 新增 get_all_links(仿照 get_all_tags),实现之初即返回 sorted(all_links)
2020-07-02 bd6cad6f get_commodity_map 简化并改名 get_commodity_directives:删掉从 posting/Balance/Price 收集币种以及自动补建 Commodity 指令的逻辑,提交信息称改名是为避免外部代码"possible subtle breakage"
2020-10-29 22ab398425f43f2b 为 issue #570 加回 get_commodity_map 兼容 stub,同日撤销;撤销信息称改名是有意为之,因为行为不同
2024-11-09 a9fd82def from __future__ import annotationsTYPE_CHECKING 块;顶层函数补类型注解;get_all_payees 返回处加 # type: ignore[arg-type]
2024-12-16 955876a9 扩充 TYPE_CHECKING 块(补 Account/Directive/Pad 等类型)并新增 AccountsUseMap 别名;GetAccounts 各方法与部分顶层函数补齐注解,get_dict_accountsget_account_open_closeget_values_meta 仍无类型注解;ACCOUNT_LABEL 赋值处加 # type: ignore[attr-defined]
2024-12-23 0f879811 为启用 mypy,AccountsUseMap 暂改用 typing.Dict/typing.Tuple 而非内置泛型
2024-12-23 8108c924 Python 基线提到 3.9,AccountsUseMap 改回 PEP 585 内置泛型;get_accounts 返回类型改为 set[str] 并在实现里包一层 set(...)

9. 与其他模块的关系

10. 参考索引

beancount/core/getters.py:1-3 模块 docstring;10-18 导入;20-34 TYPE_CHECKINGAccountsUseMap;37-38 类定义;40-57 get_accounts_use_map 方法;51-56 遍历主体;59-71 get_entry_accounts 方法;62-63 作者注释;73-82 Transaction;84-92 Pad;94-102 _one;104-112 _zero;114-116 分发表;119-120 单例;123-132 顶层 get_accounts_use_map;135-144 get_accounts;147-158 顶层 get_entry_accounts;161-174 get_account_components;177-191 get_all_tags;194-208 get_all_payees;211-225 get_all_links;228-245 get_leveln_parent_accounts;248-263 get_dict_accounts;266 ACCOUNT_LABEL;269-295 get_min_max_dates;298-314 get_active_years;317-342 get_account_open_close;345-353 get_commodity_directives;356-382 get_values_meta

beancount/core/getters_test.py:12-43 TEST_INPUT;46 测试类;47-50、52-74、76-88、90-97、99-102、104-107、109-112、114-130、132-188(170 "Wrong order here"、182 assertNotEqual)、190-194、196-199、201-214、216-229、231-244、246-252、254-258、260-266。

其它beancount/core/data.py:452-465 ALL_DIRECTIVES、:504 Directives、:309-310,423-424 Note/Document 的 tags/links、:445-448 Custom.values;beancount/core/account.py:29 sep、:37-38 组件正则、:44-46 TYPE 哑对象、:98-106 splitbeancount/api.py:60-61,148,153beancount/core/interpolate.py:337-339beancount/loader.py:596-602,738-740 排序保障;beancount/ops/validation.py:160-191 validate_duplicate_commodities:226beancount/ops/balance.py:83,90beancount/ops/summarize.py:721-722beancount/ops/documents.py:55beancount/core/realization.py:45-66 RealAccount:211-267 realize/树构建、:278-315,309-313 postings_by_account 对 Custom 的处理;beancount/plugins/auto_accounts.py:32-36beancount/plugins/leafonly.py:47-51beancount/plugins/nounused.py:45beancount/plugins/check_average_cost.py:62beancount/projects/export.py:60-71,74-80,85beancount/parser/context.py:105beancount/scripts/doctor.py:526-531beancount/scripts/directories.py:65beancount/scripts/example.py:1665CHANGES:1360-1362,4086-4094,4096-4099

commit3c568161(2013-07-05)、ba76130a(2014-06-21)、ade7e62a(2014-08-31)、b2e4e387(2015-01-04)、96e6bb13(2015-01-17)、a3c06172(2015-11-28)、376a3ccc(2016-02-28)、4489fcb2(2017-05-08)、b66f8bee(2018-03-10)、6e97fe97(2018-03-11)、4e5b638f(2018-03-27)、bd6cad6f(2020-07-02)、22ab398425f43f2b(2020-10-29)、a9fd82def(2024-11-09)、955876a9(2024-12-16)、0f879811(2024-12-23,先)、8108c924(2024-12-23,后,0f879811 的子孙提交)。