Skip to content

Commit 25bafde

Browse files
committed
add dict-based constructor
1 parent 1c12eaa commit 25bafde

File tree

9 files changed

+304
-58
lines changed

9 files changed

+304
-58
lines changed

absbox/local/base.py

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -99,14 +99,14 @@
9999

100100

101101
datePattern = {"月末": "MonthEnd", "季度末": "QuarterEnd", "年末": "YearEnd", "月初": "MonthFirst",
102-
"季度初": "QuarterFirst", "年初": "YearFirst", "每年": "MonthDayOfYear", "每月": "DayOfMonth", "每周": "DayOfWeek"}
102+
"季度初": "QuarterFirst", "年初": "YearFirst", "每年": "MonthDayOfYear", "每月": "DayOfMonth", "每周": "DayOfWeek"}
103103

104104
freqMap = {"每月": "Monthly", "每季度": "Quarterly", "每半年": "SemiAnnually", "每年": "Annually", "Monthly": "Monthly", "Quarterly": "Quarterly", "SemiAnnually": "SemiAnnually", "Annually": "Annually", "monthly": "Monthly", "quarterly": "Quarterly", "semiAnnually": "SemiAnnually", "annually": "Annually"
105-
,"Weekly":"Weekly","weekly":"Weekly","每周":"Weekly","BiWeekly":"BiWeekly","biweekly":"BiWeekly","每两周":"BiWeekly"}
105+
,"Weekly":"Weekly","weekly":"Weekly","每周":"Weekly","BiWeekly":"BiWeekly","biweekly":"BiWeekly","每两周":"BiWeekly"}
106106

107107
rateLikeFormula = set(["bondFactor", "bondFactorOf", "poolFactor", "cumPoolDefaultedRate","poolWaRate","bondWaRate", "资产池累计违约率", "债券系数", "资产池系数"
108-
,"cumPoolNetLossRate","cumPoolDefaultedRateTill","比例","ratio","avgRatio"
109-
,"平均比例","irrOfBond"])
108+
,"cumPoolNetLossRate","cumPoolDefaultedRateTill","比例","ratio","avgRatio"
109+
,"平均比例","irrOfBond"])
110110
intLikeFormula = set(["borrowerNumber", "monthsTillMaturity", "periodNum"])
111111
boolLikeFormula = set(["trigger", "事件", "isMostSenior", "最优先", "isPaidOff","清偿完毕","rateTest","allTest","anyTest","比率测试","任一测试","所有测试"
112112
"isOutstanding","isAnyOutstanding"])
@@ -122,16 +122,16 @@
122122

123123
#Deal Cycle
124124
chinaDealCycle = {"回收后":"EndCollection"
125-
,"回收动作后":"EndCollectionWF"
126-
,"分配前":"BeginDistributionWF"
127-
,"分配后":"EndDistributionWF"
128-
,"分配中":"InWF"}
125+
,"回收动作后":"EndCollectionWF"
126+
,"分配前":"BeginDistributionWF"
127+
,"分配后":"EndDistributionWF"
128+
,"分配中":"InWF"}
129129

130130
englishDealCycle = {"BeforeCollect":"EndCollection"
131-
,"AfterCollect":"EndCollectionWF"
132-
,"BeforeDistribution":"BeginDistributionWF"
133-
,"AfterDistribution":"EndDistributionWF"
134-
,"InDistribution":"InWF"}
131+
,"AfterCollect":"EndCollectionWF"
132+
,"BeforeDistribution":"BeginDistributionWF"
133+
,"AfterDistribution":"EndDistributionWF"
134+
,"InDistribution":"InWF"}
135135

136136
dealCycleMap = chinaDealCycle | englishDealCycle
137137

@@ -156,7 +156,7 @@
156156
}
157157

158158
dealStatusLog = {'cn': [china_date, "旧状态", "新状态", "备注"]
159-
,'en': [english_date, "From", "To", "Comment"]}
159+
,'en': [english_date, "From", "To", "Comment"]}
160160

161161
dealStatusMap = {"en": {'amort': "Amortizing", 'def': "Defaulted", 'acc': "Accelerated", 'end': "Ended",
162162
'called': "Called",

absbox/local/cf.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ def patchMissingIndex(df,idx:set)-> pd.DataFrame:
103103
missingIdx = idx - curIdx
104104
return df.reindex(df.index.values.tolist()+list(missingIdx)).sort_index()
105105

106+
106107
def buildJointCf(m:dict, popColumns=[]) -> pd.DataFrame:
107108
if len(m)==0:
108109
return pd.DataFrame()
@@ -148,6 +149,7 @@ def readPoolsCf(pMap) -> pd.DataFrame:
148149
df.columns = headerIndex
149150
return df
150151

152+
151153
def readTriggers(tMap) -> pd.DataFrame:
152154
''' read a map of triggers in dataframes to a single dataframe, with key as 1st level index'''
153155
if tMap == {} or tMap is None:

absbox/local/component.py

Lines changed: 97 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,15 @@
1010
from rich.console import Console
1111

1212

13-
from .interface import mkTag,readAeson
13+
from .interface import mkTag,readAeson,mkCurve
1414
from .util import mkTs, readTagStr, subMap, subMap2, renameKs, ensure100
1515
from .util import mapListValBy, uplift_m_list, mapValsBy, allList, getValWithKs, applyFnToKey,flat
1616
from .util import earlyReturnNone, mkFloatTs, mkRateTs, mkRatioTs, mkTbl, mapNone, guess_pool_flow_header
1717
from .util import filter_by_tags, enumVals, lmap, readTagMap, patchDicts,updateKs, ensureKeysInMap
1818
from .base import *
1919

20-
from ..validation import vDict, vList, vStr, vNum, vInt, vDate, vFloat, vBool, vTuple, vListOfList
21-
20+
from ..validation import vDict, vList, vStr, vNum, vInt, vDate, vFloat, vBool, vTuple, vListOfList, vListOfDict, vMap, vEnum, vOr, vOpt, vAny
21+
from ..validation import isListOfDict
2222

2323
numVal = Or(float,int)
2424
console = Console()
@@ -180,27 +180,45 @@ def mkDsRate(x):
180180

181181
def mkFeeType(x):
182182
match x:
183-
case {"年化费率": [base, rate]} | {"annualPctFee": [base, rate]} | ("annualPctFee", base, rate):
183+
case {"年化费率": [base, rate]} | {"annualPctFee": [base, rate]} | ("annualPctFee", base, rate) | {"base":base,"annualisedRate": rate}:
184184
return mkTag(("AnnualRateFee", [mkDs(base), mkDsRate(rate)]))
185-
case {"百分比费率": [base, _rate]} | {"pctFee": [base, _rate]} | ("pctFee", base, _rate):
185+
case {"百分比费率": [base, _rate]} | {"pctFee": [base, _rate]} | ("pctFee", base, _rate) | {"base":base,"pctFeeRate": _rate}:
186186
rate = mkDsRate(_rate)
187187
return mkTag(("PctFee", [mkDs(base), rate]))
188188
case {"固定费用": amt} | {"fixFee": amt} | ("fixFee", amt):
189189
return mkTag(("FixFee", vNum(amt)))
190-
case {"周期费用": [p, amt]} | {"recurFee": [p, amt]} | ("recurFee", p, amt):
190+
case {"周期费用": [p, amt]} | {"recurFee": [p, amt]} | ("recurFee", p, amt) | {"recurFeeAmt": amt, "recurDates": p}:
191191
return mkTag(("RecurFee", [mkDatePattern(p), vNum(amt)]))
192+
case {"customFee": fflow} if isListOfDict(fflow):
193+
vs = [ [m['date'],m['amount']] for m in fflow ]
194+
return mkTag(("FeeFlow", mkTs("BalanceCurve", vs)))
192195
case {"自定义": fflow} | {"customFee": fflow} | ("customFee", fflow):
193196
return mkTag(("FeeFlow", mkTs("BalanceCurve", fflow)))
194-
case {"计数费用": [p, s, amt]} | {"numFee": [p, s, amt]} | ("numFee", p, s, amt):
197+
case {"计数费用": [p, s, amt]} | {"numFee": [p, s, amt]} | ("numFee", p, s, amt) | {"numFeeDates": p, "base": s, "numFeeAmount": amt}:
195198
return mkTag(("NumFee", [mkDatePattern(p), mkDs(s), amt]))
199+
200+
case {"targetBalDue": ds1, "targetBalOffset": ds2 }:
201+
return mkTag(("TargetBalanceFee", [mkDs(ds1), mkDs(ds2)]))
196202
case {"差额费用": [ds1, ds2]} | {"targetBalanceFee": [ds1, ds2]} | ("targetBalanceFee", ds1, ds2):
197203
return mkTag(("TargetBalanceFee", [mkDs(ds1), mkDs(ds2)]))
204+
198205
case {"回款期间费用": amt} | {"byPeriod": amt} | ("byPeriod", amt):
199206
return mkTag(("ByCollectPeriod", amt))
207+
208+
case {"tableDates":dp,"tableRef":ds,"table":tbl} :
209+
return mkTag(("AmtByTbl", [mkDatePattern(dp), mkDs(ds), tbl]))
210+
200211
case {"分段费用": [dp, ds, tbl]} | {"byTable": [dp, ds, tbl]} | ("byTable", dp, ds, tbl):
201212
return mkTag(("AmtByTbl", [mkDatePattern(dp), mkDs(ds), tbl]))
213+
214+
case {"flowByBondPeriod": fflow } if isListOfDict(fflow):
215+
vs = [ [m['index'],m['amount']] for m in fflow ]
216+
return mkTag(("FeeFlowByBondPeriod", mkTs("CurrentVal", vs)))
202217
case {"flowByBondPeriod": curve} | ("flowByBondPeriod", curve):
203218
return mkTag(("FeeFlowByBondPeriod", mkTag(("CurrentVal", curve))))
219+
case {"flowByPoolPeriod": fflow } if isListOfDict(fflow):
220+
vs = [ [m['index'],m['amount']] for m in fflow ]
221+
return mkTag(("FeeFlowByPoolPeriod", mkTs("CurrentVal", vs)))
204222
case {"flowByPoolPeriod": curve} | ("flowByPoolPeriod", curve):
205223
return mkTag(("FeeFlowByPoolPeriod", mkTag(("CurrentVal", curve))))
206224
case _:
@@ -494,8 +512,6 @@ def mkDs(x):
494512
except TypeError as e:
495513
raise RuntimeError(f"Failed to match DS/Formula: {x}", e)
496514

497-
def mkCurve(tag, xs):
498-
return mkTag((tag, xs))
499515

500516

501517
def mkPre(p):
@@ -667,9 +683,9 @@ def mkBondType(x):
667683

668684
def mkBondIoItype(x):
669685
match x:
670-
case ("上浮", f) | ("inflate", f):
686+
case ("上浮", f) | ("inflate", f) | {"inflate": f}:
671687
return mkTag(("OverCurrRateBy", vNum(f)))
672-
case ("利差", spd) | ("spread", spd):
688+
case ("利差", spd) | ("spread", spd) | {"spread": spd}:
673689
return mkTag(("OverFixSpread", vNum(spd)))
674690
case _:
675691
raise RuntimeError(f"Failed to match bond IoI type:{x}")
@@ -684,9 +700,10 @@ def mkBondRate(x:dict)->dict:
684700
return mkTag(("Floater", [r, _index, Spread, mkDatePattern(resetInterval), dc, None, None]))
685701
# floater rate with default daycount
686702
case {"浮动": [r, _index, Spread, resetInterval]} | \
687-
{"floater": [r, _index, Spread, resetInterval]} | \
688-
("floater", (r, _index, Spread, resetInterval)) :
703+
{"floater": [r, _index, Spread, resetInterval]} | \
704+
("floater", (r, _index, Spread, resetInterval)) :
689705
return mkTag(("Floater", [r, _index, Spread, mkDatePattern(resetInterval), DC.DC_ACT_365F.value, None, None]))
706+
690707
# fix rate with day count
691708
case {"固定": _rate, "日历": dc} | {"fix": _rate, "dayCount": dc}:
692709
return mkTag(("Fix", [vNum(_rate), dc]))
@@ -696,31 +713,50 @@ def mkBondRate(x:dict)->dict:
696713
# fix rate with default day count
697714
case {"固定": _rate} | {"Fixed": _rate} | {"fix": _rate} | ("fix", _rate) | ["fix", _rate]:
698715
return mkTag(("Fix", [vNum(_rate), DC.DC_ACT_365F.value]))
699-
case {"期间收益": _yield} | {"interimYield": _yield}:
700-
return mkTag(("InterestByYield", vNum(_yield)))
701-
case ("refBalance", ds, ii):
716+
717+
case ("refBalance", ds, ii) | {"refBalance": ds, "rateType": ii}:
702718
return mkTag(("RefBal", [mkDs(ds), mkBondRate(ii)]))
719+
case ("ref", _rate, ds, factor, reset) | {"rate":_rate, "refRate": ds, "factor": factor, "reset": reset}: # | RefRate IRate DealStats Float RateReset
720+
return mkTag(("RefRate", [vNum(_rate), mkDs(ds), vNum(factor), mkDatePattern(reset)]))
721+
703722
# rate with cap
704723
case ("上限", cap, br) | ("cap", cap, br):
705724
return mkTag(("CapRate", [mkBondRate(br), vNum(cap)]))
706725
# rate with floor
707726
case ("下限", floor, br) | ("floor", floor, br):
708727
return mkTag(("FloorRate", [mkBondRate(br), vNum(floor)]))
709-
case ("罚息", pRateInfo,bRateInfo) | ("withIntOverInt", pRateInfo, bRateInfo):
728+
729+
case ("罚息", pRateInfo,bRateInfo) | ("withIntOverInt", pRateInfo, bRateInfo)\
730+
| {"intOverInt": pRateInfo, "rateType": bRateInfo} :
710731
return mkTag(("WithIoI", [mkBondRate(bRateInfo), mkBondIoItype(pRateInfo)]))
711-
case ("ref", _rate, ds, factor, reset): # | RefRate IRate DealStats Float RateReset
712-
return mkTag(("RefRate", [vNum(_rate), mkDs(ds), vNum(factor), mkDatePattern(reset)]))
732+
713733
case None :
714-
return mkTag(("Fix",[0, DC.DC_ACT_365F]))
734+
return mkTag(("Fix",[0, DC.DC_ACT_365F]))
735+
736+
case {"rate": rate,"index": idx, "spread": spd, "reset": resetInterval, **p} :
737+
dc = p.get("dayCount", DC.DC_ACT_365F.value)
738+
cap = p.get("cap", None)
739+
floor = p.get("floor", None)
740+
match (cap,floor):
741+
case (None, None):
742+
return mkTag(("Floater", [vNum(rate), idx, vNum(spd), mkDatePattern(resetInterval), dc, None, None]))
743+
case (None, floor):
744+
return mkBondRate(("floor", floor, tz.dissoc(x,'floor')))
745+
case (cap, None):
746+
return mkBondRate(("cap", cap, tz.dissoc(x,'cap')))
747+
case _ :
748+
raise RuntimeError(f"Failed to match bond rate with both cap and floor: {x}")
749+
750+
715751
case _:
716752
raise RuntimeError(f"Failed to match bond rate type:{x}")
717753

718754

719755
def mkStepUp(x):
720756
match x:
721-
case ("ladder", d, spd, dp):
757+
case ("ladder", d, spd, dp) | {"date": d, "spread": spd, "changeDates": dp}:
722758
return mkTag(("PassDateLadderSpread", [vDate(d), vNum(spd), mkDatePattern(dp)]))
723-
case ("once", d, spd):
759+
case ("once", d, spd) | {"date": d, "spread": spd}:
724760
return mkTag(("PassDateSpread", [vDate(d), vNum(spd)]))
725761
case _:
726762
raise RuntimeError(f"Failed to match bond step up type:{x}")
@@ -783,13 +819,13 @@ def mkBnd(bn, x:dict):
783819
mStmt = mkTxn("bond", mTxns) if mTxns else None
784820
match x:
785821
case {"balance": bndBalance
786-
, "rates": bndRates
787-
, "rateTypes": bndInterestInfos
788-
, "bondType": bndType
789-
, "originBalance": originBalance
790-
, "originRate": originRate
791-
, "startDate": originDate
792-
}:
822+
, "rates": bndRates
823+
, "rateTypes": bndInterestInfos
824+
, "bondType": bndType
825+
, "originBalance": originBalance
826+
, "originRate": originRate
827+
, "startDate": originDate
828+
}:
793829
l = len(bndInterestInfos)
794830
dueInts = getValWithKs(x, ["应付利息", "dueInt"], defaultReturn=[0]*l)
795831
dueIntOverInts = getValWithKs(x, ["拖欠利息", "dueIntOverInt"], defaultReturn=[0]*l)
@@ -830,15 +866,15 @@ def mkBnd(bn, x:dict):
830866

831867
def mkLiqMethod(x):
832868
match x:
833-
case ["正常|违约", a, b] | ["Current|Defaulted", a, b]:
869+
case ["正常|违约", a, b] | ["Current|Defaulted", a, b] | {"currentFactor": a, "defaultFactor": b}:
834870
return mkTag(("BalanceFactor", [vNum(a), vNum(b)]))
835871
case ["正常|拖欠|违约", a, b, c] | ["Current|Delinquent|Defaulted", a, b, c]:
836872
return mkTag(("BalanceFactor2", [vNum(a), vNum(b), vNum(c)]))
837873
case ["贴现|违约", a, b] | ["PV|Defaulted", a, b]:
838874
return mkTag(("PV", [a, b]))
839875
case ["贴现曲线", ts] | ["PVCurve", ts]:
840876
return mkTag(("PVCurve", mkTs("PricingCurve", ts)))
841-
case ["贴现率", r] | ["PvRate", r] | ("PvRate", r) if isinstance(r, float):
877+
case ["贴现率", r] | ["PvRate", r] | ("PvRate", r) | {"PvRate": r} if isinstance(r, float):
842878
return mkTag(("PvRate", r))
843879
case ["贴现率", r] | ["PvRate", r] | ("PvRate", r):
844880
return mkTag(("PvByRef", mkDs(r)))
@@ -988,25 +1024,34 @@ def mkRateCap(x):
9881024
def mkRateType(x):
9891025
match x :
9901026
case {"fix":r, "dayCount":dc} | {"固定":r, "日历":dc} |\
991-
["fix", r, dc] | ["固定", r, dc] | ("fix", r, dc) | ("固定", r, dc):
1027+
["fix", r, dc] | ["固定", r, dc] | ("fix", r, dc) | ("固定", r, dc):
9921028
return mkTag(("Fix", [dc, vNum(r)]))
9931029
case {"fix":r} | {"固定":r} | ["fix", r] | ["固定", r] | ("fix", r) | ("固定", r):
9941030
return mkTag(("Fix", [DC.DC_ACT_365F.value, vNum(r)]))
1031+
9951032
case {"floater":(idx, spd), "rate":r, "reset":dp, **p} | \
9961033
{"浮动":(idx, spd), "利率":r, "重置":dp, **p}:
9971034
mf, mc, mrnd = tz.get(["floor", "cap", "rounding"], p, None)
9981035
dc = p.get("dayCount", DC.DC_ACT_365F.value)
9991036
return mkTag(("Floater",[dc, vStr(idx), vNum(spd), vNum(r), mkDatePattern(dp), mf, mc, mrnd]))
1037+
1038+
case {"index":idx,"spread":spd,"reset":dp, "rate":r, **p} :
1039+
mf, mc, mrnd = tz.get(["floor", "cap", "rounding"], p, None)
1040+
dc = p.get("dayCount", DC.DC_ACT_365F.value)
1041+
return mkTag(("Floater",[dc, vStr(idx), vNum(spd), vNum(r), mkDatePattern(dp), mf, mc, mrnd]))
1042+
10001043
case ["浮动", r, {"基准":idx, "利差":spd, "重置频率":dp, **p}] | \
10011044
["floater", r, {"index":idx, "spread":spd, "reset":dp, **p}] :
10021045
mf, mc, mrnd = tz.get(["floor", "cap", "rounding"], p, None)
10031046
dc = p.get("dayCount", DC.DC_ACT_365F.value)
10041047
return mkTag(("Floater", [dc, vStr(idx), vNum(spd), vNum(r), mkDatePattern(dp), mf, mc, mrnd]))
1048+
10051049
case ("浮动", r, {"基准":idx, "利差":spd, "重置频率":dp, **p}) | \
10061050
("floater", r, {"index":idx, "spread":spd, "reset":dp, **p}) :
10071051
mf, mc, mrnd = tz.get(["floor", "cap", "rounding"], p, None)
10081052
dc = p.get("dayCount", DC.DC_ACT_365F.value)
10091053
return mkTag(("Floater", [dc, vStr(idx), vNum(spd), vNum(r), mkDatePattern(dp), mf, mc, mrnd]))
1054+
10101055
case None:
10111056
return None
10121057
case _ :
@@ -1303,6 +1348,8 @@ def mkStatus(x: tuple|str):
13031348
return mkTag(("DealDefaulted", None))
13041349
case "结束" | "Ended":
13051350
return mkTag(("Ended", None))
1351+
case {"current": "PreClosing", "next":st}:
1352+
return mkTag(("PreClosing", mkStatus(st)))
13061353
case ("设计", st) | ("PreClosing", st) | ("preclosing", st):
13071354
return mkTag(("PreClosing", mkStatus(st)))
13081355
case _:
@@ -2220,14 +2267,32 @@ def mkCollection(x):
22202267
return mkTag(("Collect",[None, mkPoolSource(s), acc]))
22212268
case [s, *pcts] if isinstance(pcts, list) and isinstance(s, str):
22222269
return mkTag(("CollectByPct" ,[None, mkPoolSource(s), pcts]))
2270+
22232271
case [None, s, acc] if isinstance(acc, str):
22242272
return mkTag(("Collect",[None, mkPoolSource(s), acc]))
22252273
case [None, s, *pcts] if isinstance(pcts, list):
22262274
return mkTag(("CollectByPct" ,[None, mkPoolSource(s), pcts]))
2275+
22272276
case [mPids, s, acc] if isinstance(acc, str):
22282277
return mkTag(("Collect",[lmap(mkPid,mPids), mkPoolSource(s), acc]))
22292278
case [mPids, s, *pcts] if isinstance(pcts, list):
22302279
return mkTag(("CollectByPct" ,[lmap(mkPid,mPids), mkPoolSource(s), pcts]))
2280+
2281+
case {"source":s, "account":acc}:
2282+
if(_pids:=x.get("poolId", False)):
2283+
pids = lmap(mkPid, _pids)
2284+
else:
2285+
pids = None
2286+
return mkTag(("Collect",[pids, mkPoolSource(s), acc]))
2287+
2288+
case {"source":s, "accountByPct":accs}:
2289+
if(_pids:=x.get("poolId", False)):
2290+
pids = lmap(mkPid, _pids)
2291+
else:
2292+
pids = None
2293+
accsWithPct = [ [vNum(accPct),vStr(accName)] for accName,accPct in accs.items() ]
2294+
return mkTag(("CollectByPct",[pids, mkPoolSource(s), accsWithPct]))
2295+
22312296
case _:
22322297
raise RuntimeError(f"Failed to match collection rule {x}")
22332298

absbox/local/interface.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@ def mkTag(x: tuple | str) -> dict:
99
case (tagName):
1010
return {"tag": tagName}
1111

12+
def mkCurve(tag, xs):
13+
return mkTag((tag, xs))
14+
15+
16+
17+
1218
def readAeson(x:dict):
1319
match x:
1420
case {"tag": tag,"contents": contents} if isinstance(contents,list):

absbox/local/util.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -463,3 +463,19 @@ def patchDicts(dict1:dict,dict2:dict)-> dict:
463463
def getNumCols(df:pd.DataFrame)-> list:
464464
numeric_columns = [col for col in df.columns if pd.to_numeric(df[col], errors='coerce').notna().all()]
465465
return numeric_columns
466+
467+
468+
469+
def genDescList(lst, n, reverse=False):
470+
"""
471+
Return a list of lists, where the first element is the original list,
472+
and each subsequent list is the previous one with its last element removed.
473+
The process stops after producing n lists.
474+
"""
475+
# Create an infinite sequence: original, then without last, then without last, ...
476+
assert len(lst) >= n, f"List length should be less than n, got {len(lst)} and n={n}"
477+
seq = tz.iterate(lambda x: x[:-1], lst)
478+
# Take the first n items and convert to a list
479+
if reverse:
480+
return list(reversed(list(tz.take(n, seq))))
481+
return list(tz.take(n, seq))

docs/source/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
copyright = '2025, Xiaoyu Zhang'
1010
author = 'Xiaoyu Zhang'
1111

12-
release = "0.52.0"
12+
release = "0.52.3"
1313

1414
# -- General configuration
1515

0 commit comments

Comments
 (0)