lib-x17-fintech 2.1.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- lib_x17_fintech-2.1.3.dist-info/METADATA +633 -0
- lib_x17_fintech-2.1.3.dist-info/RECORD +282 -0
- lib_x17_fintech-2.1.3.dist-info/WHEEL +5 -0
- lib_x17_fintech-2.1.3.dist-info/licenses/LICENSE +1 -0
- lib_x17_fintech-2.1.3.dist-info/top_level.txt +1 -0
- xfintech/__init__.py +0 -0
- xfintech/connect/__init__.py +18 -0
- xfintech/connect/artifact/__init__.py +5 -0
- xfintech/connect/artifact/artifact.py +168 -0
- xfintech/connect/artifact/tests/__init__.py +3 -0
- xfintech/connect/artifact/tests/test_class_artifact_all.py +564 -0
- xfintech/connect/common/__init__.py +12 -0
- xfintech/connect/common/connect.py +49 -0
- xfintech/connect/common/connectref.py +119 -0
- xfintech/connect/common/error.py +62 -0
- xfintech/connect/common/tests/__init__.py +1 -0
- xfintech/connect/common/tests/test_class_connectlike_all.py +544 -0
- xfintech/connect/common/tests/test_class_connectref_all.py +586 -0
- xfintech/connect/common/tests/test_class_errors_all.py +524 -0
- xfintech/connect/instance/__init__.py +7 -0
- xfintech/connect/instance/macos.py +121 -0
- xfintech/connect/instance/s3.py +176 -0
- xfintech/connect/instance/tests/__init__.py +1 -0
- xfintech/connect/instance/tests/test_class_macosconnect_all.py +692 -0
- xfintech/connect/instance/tests/test_class_s3connect_all.py +603 -0
- xfintech/data/__init__.py +20 -0
- xfintech/data/common/__init__.py +15 -0
- xfintech/data/common/cache.py +186 -0
- xfintech/data/common/coolant.py +171 -0
- xfintech/data/common/metric.py +138 -0
- xfintech/data/common/paginate.py +132 -0
- xfintech/data/common/params.py +162 -0
- xfintech/data/common/retry.py +201 -0
- xfintech/data/common/tests/__init__.py +1 -0
- xfintech/data/common/tests/test_class_cache_all.py +681 -0
- xfintech/data/common/tests/test_class_coolant_all.py +534 -0
- xfintech/data/common/tests/test_class_metric_all.py +705 -0
- xfintech/data/common/tests/test_class_paginate_all.py +508 -0
- xfintech/data/common/tests/test_class_params_all.py +891 -0
- xfintech/data/common/tests/test_class_retry_all.py +714 -0
- xfintech/data/job/__init__.py +17 -0
- xfintech/data/job/errors.py +112 -0
- xfintech/data/job/house.py +156 -0
- xfintech/data/job/job.py +247 -0
- xfintech/data/job/joblike.py +47 -0
- xfintech/data/job/tests/__init__.py +1 -0
- xfintech/data/job/tests/test_class_errors_all.py +275 -0
- xfintech/data/job/tests/test_class_house_all.py +801 -0
- xfintech/data/job/tests/test_class_job_all.py +684 -0
- xfintech/data/job/tests/test_class_joblike_all.py +482 -0
- xfintech/data/relay/__init__.py +7 -0
- xfintech/data/relay/client.py +114 -0
- xfintech/data/relay/clientlike.py +45 -0
- xfintech/data/relay/tests/test_class_relayclient_all.py +484 -0
- xfintech/data/relay/tests/test_class_relayclientlike_all.py +500 -0
- xfintech/data/source/__init__.py +7 -0
- xfintech/data/source/baostock/__init__.py +21 -0
- xfintech/data/source/baostock/job/__init__.py +5 -0
- xfintech/data/source/baostock/job/job.py +217 -0
- xfintech/data/source/baostock/job/tests/__init__.py +0 -0
- xfintech/data/source/baostock/job/tests/test_class_baostockjob_all.py +547 -0
- xfintech/data/source/baostock/session/__init__.py +8 -0
- xfintech/data/source/baostock/session/relay.py +223 -0
- xfintech/data/source/baostock/session/session.py +241 -0
- xfintech/data/source/baostock/session/tests/__init__.py +0 -0
- xfintech/data/source/baostock/session/tests/test_class_relay_all.py +694 -0
- xfintech/data/source/baostock/session/tests/test_class_session_all.py +505 -0
- xfintech/data/source/baostock/stock/__init__.py +0 -0
- xfintech/data/source/baostock/stock/hs300stock/__init__.py +3 -0
- xfintech/data/source/baostock/stock/hs300stock/constant.py +49 -0
- xfintech/data/source/baostock/stock/hs300stock/hs300stock.py +133 -0
- xfintech/data/source/baostock/stock/hs300stock/tests/__init__.py +1 -0
- xfintech/data/source/baostock/stock/hs300stock/tests/test_class_hs300index_all.py +413 -0
- xfintech/data/source/baostock/stock/minuteline/__init__.py +19 -0
- xfintech/data/source/baostock/stock/minuteline/constant.py +89 -0
- xfintech/data/source/baostock/stock/minuteline/minuteline.py +163 -0
- xfintech/data/source/baostock/stock/minuteline/tests/__init__.py +0 -0
- xfintech/data/source/baostock/stock/minuteline/tests/test_class_minuteline_all.py +582 -0
- xfintech/data/source/baostock/stock/stock/__init__.py +19 -0
- xfintech/data/source/baostock/stock/stock/constant.py +55 -0
- xfintech/data/source/baostock/stock/stock/stock.py +149 -0
- xfintech/data/source/baostock/stock/stock/tests/__init__.py +0 -0
- xfintech/data/source/baostock/stock/stock/tests/test_class_stock_all.py +508 -0
- xfintech/data/source/baostock/stock/stockinfo/__init__.py +5 -0
- xfintech/data/source/baostock/stock/stockinfo/constant.py +66 -0
- xfintech/data/source/baostock/stock/stockinfo/stockinfo.py +176 -0
- xfintech/data/source/baostock/stock/stockinfo/tests/__init__.py +1 -0
- xfintech/data/source/baostock/stock/stockinfo/tests/test_class_stockinfo_all.py +617 -0
- xfintech/data/source/baostock/stock/sz50stock/__init__.py +3 -0
- xfintech/data/source/baostock/stock/sz50stock/constant.py +49 -0
- xfintech/data/source/baostock/stock/sz50stock/sz50stock.py +133 -0
- xfintech/data/source/baostock/stock/sz50stock/tests/__init__.py +1 -0
- xfintech/data/source/baostock/stock/sz50stock/tests/test_class_sz50stock_all.py +397 -0
- xfintech/data/source/baostock/stock/tradedate/__init__.py +19 -0
- xfintech/data/source/baostock/stock/tradedate/constant.py +72 -0
- xfintech/data/source/baostock/stock/tradedate/tests/__init__.py +0 -0
- xfintech/data/source/baostock/stock/tradedate/tests/test_class_tradedate_all.py +695 -0
- xfintech/data/source/baostock/stock/tradedate/tradedate.py +208 -0
- xfintech/data/source/baostock/stock/zz500stock/__init__.py +3 -0
- xfintech/data/source/baostock/stock/zz500stock/constant.py +55 -0
- xfintech/data/source/baostock/stock/zz500stock/tests/__init__.py +1 -0
- xfintech/data/source/baostock/stock/zz500stock/tests/test_class_zz500stock_all.py +421 -0
- xfintech/data/source/baostock/stock/zz500stock/zz500stock.py +133 -0
- xfintech/data/source/tushare/__init__.py +61 -0
- xfintech/data/source/tushare/job/__init__.py +5 -0
- xfintech/data/source/tushare/job/job.py +257 -0
- xfintech/data/source/tushare/job/tests/test_class_tusharejob_all.py +589 -0
- xfintech/data/source/tushare/session/__init__.py +5 -0
- xfintech/data/source/tushare/session/relay.py +231 -0
- xfintech/data/source/tushare/session/session.py +239 -0
- xfintech/data/source/tushare/session/tests/test_class_relay_all.py +719 -0
- xfintech/data/source/tushare/session/tests/test_class_session_all.py +705 -0
- xfintech/data/source/tushare/stock/__init__.py +55 -0
- xfintech/data/source/tushare/stock/adjfactor/__init__.py +19 -0
- xfintech/data/source/tushare/stock/adjfactor/adjfactor.py +150 -0
- xfintech/data/source/tushare/stock/adjfactor/constant.py +71 -0
- xfintech/data/source/tushare/stock/adjfactor/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/adjfactor/tests/test_class_adjfactor_all.py +372 -0
- xfintech/data/source/tushare/stock/capflow/__init__.py +19 -0
- xfintech/data/source/tushare/stock/capflow/capflow.py +171 -0
- xfintech/data/source/tushare/stock/capflow/constant.py +105 -0
- xfintech/data/source/tushare/stock/capflow/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/capflow/tests/test_class_capflow_all.py +589 -0
- xfintech/data/source/tushare/stock/capflowdc/__init__.py +19 -0
- xfintech/data/source/tushare/stock/capflowdc/capflowdc.py +167 -0
- xfintech/data/source/tushare/stock/capflowdc/constant.py +95 -0
- xfintech/data/source/tushare/stock/capflowdc/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/capflowdc/tests/test_class_capflowdc_all.py +814 -0
- xfintech/data/source/tushare/stock/capflowths/__init__.py +19 -0
- xfintech/data/source/tushare/stock/capflowths/capflowths.py +173 -0
- xfintech/data/source/tushare/stock/capflowths/constant.py +92 -0
- xfintech/data/source/tushare/stock/capflowths/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/capflowths/tests/test_class_capflowths_all.py +551 -0
- xfintech/data/source/tushare/stock/company/__init__.py +19 -0
- xfintech/data/source/tushare/stock/company/company.py +188 -0
- xfintech/data/source/tushare/stock/company/constant.py +92 -0
- xfintech/data/source/tushare/stock/company/tests/__init__.py +1 -0
- xfintech/data/source/tushare/stock/company/tests/test_class_company_all.py +829 -0
- xfintech/data/source/tushare/stock/companybusiness/__init__.py +21 -0
- xfintech/data/source/tushare/stock/companybusiness/companybusiness.py +183 -0
- xfintech/data/source/tushare/stock/companybusiness/constant.py +91 -0
- xfintech/data/source/tushare/stock/companybusiness/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/companybusiness/tests/test_class_companybusiness_all.py +633 -0
- xfintech/data/source/tushare/stock/companycashflow/__init__.py +21 -0
- xfintech/data/source/tushare/stock/companycashflow/companycashflow.py +277 -0
- xfintech/data/source/tushare/stock/companycashflow/constant.py +293 -0
- xfintech/data/source/tushare/stock/companycashflow/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/companycashflow/tests/test_class_companycashflow_all.py +619 -0
- xfintech/data/source/tushare/stock/companydebt/__init__.py +19 -0
- xfintech/data/source/tushare/stock/companydebt/companydebt.py +339 -0
- xfintech/data/source/tushare/stock/companydebt/constant.py +403 -0
- xfintech/data/source/tushare/stock/companydebt/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/companydebt/tests/test_class_companydebt_all.py +655 -0
- xfintech/data/source/tushare/stock/companyoverview/__init__.py +21 -0
- xfintech/data/source/tushare/stock/companyoverview/companyoverview.py +214 -0
- xfintech/data/source/tushare/stock/companyoverview/constant.py +152 -0
- xfintech/data/source/tushare/stock/companyoverview/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/companyoverview/tests/test_class_companyoverview_all.py +647 -0
- xfintech/data/source/tushare/stock/companyprofit/__init__.py +21 -0
- xfintech/data/source/tushare/stock/companyprofit/companyprofit.py +272 -0
- xfintech/data/source/tushare/stock/companyprofit/constant.py +259 -0
- xfintech/data/source/tushare/stock/companyprofit/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/companyprofit/tests/test_class_companyprofit_all.py +635 -0
- xfintech/data/source/tushare/stock/conceptcapflowdc/__init__.py +21 -0
- xfintech/data/source/tushare/stock/conceptcapflowdc/conceptcapflowdc.py +175 -0
- xfintech/data/source/tushare/stock/conceptcapflowdc/constant.py +106 -0
- xfintech/data/source/tushare/stock/conceptcapflowdc/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/conceptcapflowdc/tests/test_class_conceptcapflowdc_all.py +568 -0
- xfintech/data/source/tushare/stock/conceptcapflowths/__init__.py +21 -0
- xfintech/data/source/tushare/stock/conceptcapflowths/conceptcapflowths.py +188 -0
- xfintech/data/source/tushare/stock/conceptcapflowths/constant.py +89 -0
- xfintech/data/source/tushare/stock/conceptcapflowths/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/conceptcapflowths/tests/test_class_conceptcapflowths_all.py +516 -0
- xfintech/data/source/tushare/stock/dayline/__init__.py +19 -0
- xfintech/data/source/tushare/stock/dayline/constant.py +87 -0
- xfintech/data/source/tushare/stock/dayline/dayline.py +177 -0
- xfintech/data/source/tushare/stock/dayline/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/dayline/tests/test_class_dayline_all.py +585 -0
- xfintech/data/source/tushare/stock/industrycapflowths/__init__.py +21 -0
- xfintech/data/source/tushare/stock/industrycapflowths/constant.py +89 -0
- xfintech/data/source/tushare/stock/industrycapflowths/industrycapflowths.py +192 -0
- xfintech/data/source/tushare/stock/industrycapflowths/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/industrycapflowths/tests/test_class_industrycapflowths_all.py +683 -0
- xfintech/data/source/tushare/stock/marketindexcapflowdc/__init__.py +21 -0
- xfintech/data/source/tushare/stock/marketindexcapflowdc/constant.py +90 -0
- xfintech/data/source/tushare/stock/marketindexcapflowdc/marketindexcapflowdc.py +173 -0
- xfintech/data/source/tushare/stock/marketindexcapflowdc/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/marketindexcapflowdc/tests/test_class_marketindexcapflowdc_all.py +793 -0
- xfintech/data/source/tushare/stock/monthline/__init__.py +19 -0
- xfintech/data/source/tushare/stock/monthline/constant.py +87 -0
- xfintech/data/source/tushare/stock/monthline/monthline.py +180 -0
- xfintech/data/source/tushare/stock/monthline/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/monthline/tests/test_class_monthline_all.py +574 -0
- xfintech/data/source/tushare/stock/stock/__init__.py +19 -0
- xfintech/data/source/tushare/stock/stock/constant.py +105 -0
- xfintech/data/source/tushare/stock/stock/stock.py +193 -0
- xfintech/data/source/tushare/stock/stock/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/stock/tests/test_class_stock_all.py +788 -0
- xfintech/data/source/tushare/stock/stockdividend/__init__.py +21 -0
- xfintech/data/source/tushare/stock/stockdividend/constant.py +111 -0
- xfintech/data/source/tushare/stock/stockdividend/stockdividend.py +180 -0
- xfintech/data/source/tushare/stock/stockdividend/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/stockdividend/tests/test_class_stockdividend_all.py +725 -0
- xfintech/data/source/tushare/stock/stockinfo/__init__.py +19 -0
- xfintech/data/source/tushare/stock/stockinfo/constant.py +104 -0
- xfintech/data/source/tushare/stock/stockinfo/stockinfo.py +208 -0
- xfintech/data/source/tushare/stock/stockinfo/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/stockinfo/tests/test_class_stockinfo_all.py +881 -0
- xfintech/data/source/tushare/stock/stockipo/__init__.py +19 -0
- xfintech/data/source/tushare/stock/stockipo/constant.py +90 -0
- xfintech/data/source/tushare/stock/stockipo/stockipo.py +234 -0
- xfintech/data/source/tushare/stock/stockipo/tests/__init__.py +1 -0
- xfintech/data/source/tushare/stock/stockipo/tests/test_class_stockipo_all.py +750 -0
- xfintech/data/source/tushare/stock/stockpledge/__init__.py +19 -0
- xfintech/data/source/tushare/stock/stockpledge/constant.py +72 -0
- xfintech/data/source/tushare/stock/stockpledge/stockpledge.py +158 -0
- xfintech/data/source/tushare/stock/stockpledge/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/stockpledge/tests/test_class_stockpledge_all.py +664 -0
- xfintech/data/source/tushare/stock/stockpledgedetail/__init__.py +21 -0
- xfintech/data/source/tushare/stock/stockpledgedetail/constant.py +85 -0
- xfintech/data/source/tushare/stock/stockpledgedetail/stockpledgedetail.py +171 -0
- xfintech/data/source/tushare/stock/stockpledgedetail/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/stockpledgedetail/tests/test_class_stockpledgedetail_all.py +112 -0
- xfintech/data/source/tushare/stock/stockst/__init__.py +19 -0
- xfintech/data/source/tushare/stock/stockst/constant.py +80 -0
- xfintech/data/source/tushare/stock/stockst/stockst.py +189 -0
- xfintech/data/source/tushare/stock/stockst/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/stockst/tests/test_class_stockst_all.py +693 -0
- xfintech/data/source/tushare/stock/stocksuspend/__init__.py +21 -0
- xfintech/data/source/tushare/stock/stocksuspend/constant.py +75 -0
- xfintech/data/source/tushare/stock/stocksuspend/stocksuspend.py +151 -0
- xfintech/data/source/tushare/stock/stocksuspend/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/stocksuspend/tests/test_class_stocksuspend_all.py +626 -0
- xfintech/data/source/tushare/stock/techindex/__init__.py +19 -0
- xfintech/data/source/tushare/stock/techindex/constant.py +600 -0
- xfintech/data/source/tushare/stock/techindex/techindex.py +314 -0
- xfintech/data/source/tushare/stock/techindex/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/techindex/tests/test_class_techindex_all.py +576 -0
- xfintech/data/source/tushare/stock/tradedate/__init__.py +19 -0
- xfintech/data/source/tushare/stock/tradedate/constant.py +93 -0
- xfintech/data/source/tushare/stock/tradedate/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/tradedate/tests/test_class_tradedate_all.py +947 -0
- xfintech/data/source/tushare/stock/tradedate/tradedate.py +234 -0
- xfintech/data/source/tushare/stock/weekline/__init__.py +19 -0
- xfintech/data/source/tushare/stock/weekline/constant.py +87 -0
- xfintech/data/source/tushare/stock/weekline/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/weekline/tests/test_class_weekline_all.py +575 -0
- xfintech/data/source/tushare/stock/weekline/weekline.py +182 -0
- xfintech/fabric/__init__.py +18 -0
- xfintech/fabric/column/__init__.py +7 -0
- xfintech/fabric/column/info.py +202 -0
- xfintech/fabric/column/kind.py +102 -0
- xfintech/fabric/column/tests/__init__.py +0 -0
- xfintech/fabric/column/tests/test_class_info_all.py +207 -0
- xfintech/fabric/column/tests/test_class_kind_all.py +80 -0
- xfintech/fabric/table/__init__.py +5 -0
- xfintech/fabric/table/info.py +263 -0
- xfintech/fabric/table/tests/__init__.py +0 -0
- xfintech/fabric/table/tests/test_class_info_all.py +547 -0
- xfintech/serde/__init__.py +35 -0
- xfintech/serde/common/__init__.py +9 -0
- xfintech/serde/common/dataformat.py +78 -0
- xfintech/serde/common/deserialiserlike.py +38 -0
- xfintech/serde/common/error.py +182 -0
- xfintech/serde/common/serialiserlike.py +38 -0
- xfintech/serde/common/tests/__init__.py +1 -0
- xfintech/serde/common/tests/test_class_dataformat_all.py +694 -0
- xfintech/serde/common/tests/test_class_deserialiserlike_all.py +500 -0
- xfintech/serde/common/tests/test_class_errors_all.py +518 -0
- xfintech/serde/common/tests/test_class_serialiserlike_all.py +401 -0
- xfintech/serde/deserialiser/__init__.py +7 -0
- xfintech/serde/deserialiser/pandas.py +113 -0
- xfintech/serde/deserialiser/python.py +68 -0
- xfintech/serde/deserialiser/tests/__init__.py +1 -0
- xfintech/serde/deserialiser/tests/test_class_pandasdeserialiser_all.py +503 -0
- xfintech/serde/deserialiser/tests/test_class_pythondeserialiser_all.py +570 -0
- xfintech/serde/serialiser/__init__.py +7 -0
- xfintech/serde/serialiser/pandas.py +116 -0
- xfintech/serde/serialiser/python.py +71 -0
- xfintech/serde/serialiser/tests/__init__.py +1 -0
- xfintech/serde/serialiser/tests/test_class_pandasserialiser_all.py +474 -0
- xfintech/serde/serialiser/tests/test_class_pythonserialiser_all.py +508 -0
|
@@ -0,0 +1,544 @@
|
|
|
1
|
+
"""
|
|
2
|
+
描述:
|
|
3
|
+
- ConnectLike 协议的单元测试。
|
|
4
|
+
- 测试协议的结构、实现和运行时类型检查。
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
import pytest
|
|
12
|
+
|
|
13
|
+
from xfintech.connect.common.connect import ConnectLike
|
|
14
|
+
from xfintech.connect.common.connectref import ConnectRef
|
|
15
|
+
|
|
16
|
+
# =============================================================================
|
|
17
|
+
# Protocol Structure Tests
|
|
18
|
+
# =============================================================================
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def test_connectlike_is_protocol():
|
|
22
|
+
"""测试 ConnectLike 是一个协议类"""
|
|
23
|
+
from typing import Protocol
|
|
24
|
+
|
|
25
|
+
assert issubclass(ConnectLike.__class__, type(Protocol))
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def test_connectlike_is_runtime_checkable():
|
|
29
|
+
"""测试 ConnectLike 是运行时可检查的"""
|
|
30
|
+
# Check for protocol marker attributes that exist in Python 3.8+
|
|
31
|
+
assert hasattr(ConnectLike, "_is_protocol") or hasattr(ConnectLike, "__protocol_attrs__")
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def test_connectlike_has_get_object_method():
|
|
35
|
+
"""测试 ConnectLike 定义了 get_object 方法"""
|
|
36
|
+
assert hasattr(ConnectLike, "get_object")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def test_connectlike_has_put_object_method():
|
|
40
|
+
"""测试 ConnectLike 定义了 put_object 方法"""
|
|
41
|
+
assert hasattr(ConnectLike, "put_object")
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def test_connectlike_method_signatures():
|
|
45
|
+
"""测试方法签名"""
|
|
46
|
+
import inspect
|
|
47
|
+
|
|
48
|
+
# Check get_object signature
|
|
49
|
+
get_sig = inspect.signature(ConnectLike.get_object)
|
|
50
|
+
get_params = list(get_sig.parameters.keys())
|
|
51
|
+
assert "self" in get_params
|
|
52
|
+
assert "location" in get_params
|
|
53
|
+
assert "kwargs" in get_params
|
|
54
|
+
|
|
55
|
+
# Check put_object signature
|
|
56
|
+
put_sig = inspect.signature(ConnectLike.put_object)
|
|
57
|
+
put_params = list(put_sig.parameters.keys())
|
|
58
|
+
assert "self" in put_params
|
|
59
|
+
assert "data" in put_params
|
|
60
|
+
assert "location" in put_params
|
|
61
|
+
assert "kwargs" in put_params
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
# =============================================================================
|
|
65
|
+
# Implementation Tests - Valid Implementations
|
|
66
|
+
# =============================================================================
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def test_valid_implementation_basic():
|
|
70
|
+
"""测试基本的有效实现"""
|
|
71
|
+
|
|
72
|
+
class ValidConnect(ConnectLike):
|
|
73
|
+
def get_object(self, location: str, **kwargs: Any) -> bytes:
|
|
74
|
+
return b"data"
|
|
75
|
+
|
|
76
|
+
def put_object(self, data: bytes, location: str, **kwargs: Any) -> ConnectRef:
|
|
77
|
+
return ConnectRef(location=location)
|
|
78
|
+
|
|
79
|
+
instance = ValidConnect()
|
|
80
|
+
assert isinstance(instance, ConnectLike)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def test_valid_implementation_can_call_get_object():
|
|
84
|
+
"""测试有效实现可以调用 get_object"""
|
|
85
|
+
|
|
86
|
+
class ValidConnect(ConnectLike):
|
|
87
|
+
def get_object(self, location: str, **kwargs: Any) -> bytes:
|
|
88
|
+
return b"test_data"
|
|
89
|
+
|
|
90
|
+
def put_object(self, data: bytes, location: str, **kwargs: Any) -> ConnectRef:
|
|
91
|
+
return ConnectRef(location=location)
|
|
92
|
+
|
|
93
|
+
connect = ValidConnect()
|
|
94
|
+
result = connect.get_object("test_location")
|
|
95
|
+
assert result == b"test_data"
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def test_valid_implementation_can_call_put_object():
|
|
99
|
+
"""测试有效实现可以调用 put_object"""
|
|
100
|
+
|
|
101
|
+
class ValidConnect:
|
|
102
|
+
def get_object(self, location: str, **kwargs: Any) -> bytes:
|
|
103
|
+
return b"data"
|
|
104
|
+
|
|
105
|
+
def put_object(self, data: bytes, location: str, **kwargs: Any) -> ConnectRef:
|
|
106
|
+
return ConnectRef(location=location, kind="test")
|
|
107
|
+
|
|
108
|
+
connect = ValidConnect()
|
|
109
|
+
result = connect.put_object(b"test_data", "test_location")
|
|
110
|
+
assert isinstance(result, ConnectRef)
|
|
111
|
+
assert result.location == "test_location"
|
|
112
|
+
assert result.kind == "test"
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def test_valid_implementation_with_kwargs():
|
|
116
|
+
"""测试有效实现支持 kwargs"""
|
|
117
|
+
|
|
118
|
+
class ValidConnect:
|
|
119
|
+
def get_object(self, location: str, **kwargs: Any) -> bytes:
|
|
120
|
+
encoding = kwargs.get("encoding", "utf-8")
|
|
121
|
+
return f"data:{encoding}".encode()
|
|
122
|
+
|
|
123
|
+
def put_object(self, data: bytes, location: str, **kwargs: Any) -> ConnectRef:
|
|
124
|
+
kind = kwargs.get("kind", "default")
|
|
125
|
+
return ConnectRef(location=location, kind=kind)
|
|
126
|
+
|
|
127
|
+
connect = ValidConnect()
|
|
128
|
+
|
|
129
|
+
# Test get_object with kwargs
|
|
130
|
+
result = connect.get_object("location", encoding="ascii")
|
|
131
|
+
assert result == b"data:ascii"
|
|
132
|
+
|
|
133
|
+
# Test put_object with kwargs
|
|
134
|
+
ref = connect.put_object(b"data", "location", kind="custom")
|
|
135
|
+
assert ref.kind == "custom"
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def test_valid_implementation_with_state():
|
|
139
|
+
"""测试包含状态的有效实现"""
|
|
140
|
+
|
|
141
|
+
class StatefulConnect:
|
|
142
|
+
def __init__(self):
|
|
143
|
+
self.kind = "stateful"
|
|
144
|
+
self.call_count = 0
|
|
145
|
+
|
|
146
|
+
def get_object(self, location: str, **kwargs: Any) -> bytes:
|
|
147
|
+
self.call_count += 1
|
|
148
|
+
return b"data"
|
|
149
|
+
|
|
150
|
+
def put_object(self, data: bytes, location: str, **kwargs: Any) -> ConnectRef:
|
|
151
|
+
self.call_count += 1
|
|
152
|
+
return ConnectRef(location=location)
|
|
153
|
+
|
|
154
|
+
connect = StatefulConnect()
|
|
155
|
+
assert isinstance(connect, ConnectLike)
|
|
156
|
+
assert connect.call_count == 0
|
|
157
|
+
|
|
158
|
+
connect.get_object("loc")
|
|
159
|
+
assert connect.call_count == 1
|
|
160
|
+
|
|
161
|
+
connect.put_object(b"data", "loc")
|
|
162
|
+
assert connect.call_count == 2
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def test_valid_implementation_with_extra_methods():
|
|
166
|
+
"""测试包含额外方法的有效实现"""
|
|
167
|
+
|
|
168
|
+
class RichConnect:
|
|
169
|
+
def __init__(self):
|
|
170
|
+
self.kind = "rich"
|
|
171
|
+
|
|
172
|
+
def get_object(self, location: str, **kwargs: Any) -> bytes:
|
|
173
|
+
return b"data"
|
|
174
|
+
|
|
175
|
+
def put_object(self, data: bytes, location: str, **kwargs: Any) -> ConnectRef:
|
|
176
|
+
return ConnectRef(location=location)
|
|
177
|
+
|
|
178
|
+
def delete_object(self, location: str) -> None:
|
|
179
|
+
pass
|
|
180
|
+
|
|
181
|
+
def list_objects(self) -> list:
|
|
182
|
+
return []
|
|
183
|
+
|
|
184
|
+
connect = RichConnect()
|
|
185
|
+
assert isinstance(connect, ConnectLike)
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
# =============================================================================
|
|
189
|
+
# Implementation Tests - Invalid Implementations
|
|
190
|
+
# =============================================================================
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def test_invalid_implementation_missing_get_object():
|
|
194
|
+
"""测试缺少 get_object 方法的实现"""
|
|
195
|
+
|
|
196
|
+
class InvalidConnect:
|
|
197
|
+
def put_object(self, data: bytes, location: str, **kwargs: Any) -> ConnectRef:
|
|
198
|
+
return ConnectRef(location=location)
|
|
199
|
+
|
|
200
|
+
instance = InvalidConnect()
|
|
201
|
+
assert not isinstance(instance, ConnectLike)
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def test_invalid_implementation_missing_put_object():
|
|
205
|
+
"""测试缺少 put_object 方法的实现"""
|
|
206
|
+
|
|
207
|
+
class InvalidConnect:
|
|
208
|
+
def get_object(self, location: str, **kwargs: Any) -> bytes:
|
|
209
|
+
return b"data"
|
|
210
|
+
|
|
211
|
+
instance = InvalidConnect()
|
|
212
|
+
assert not isinstance(instance, ConnectLike)
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def test_invalid_implementation_missing_both():
|
|
216
|
+
"""测试缺少所有方法的实现"""
|
|
217
|
+
|
|
218
|
+
class InvalidConnect:
|
|
219
|
+
pass
|
|
220
|
+
|
|
221
|
+
instance = InvalidConnect()
|
|
222
|
+
assert not isinstance(instance, ConnectLike)
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def test_invalid_implementation_wrong_method_names():
|
|
226
|
+
"""测试方法名错误的实现"""
|
|
227
|
+
|
|
228
|
+
class InvalidConnect:
|
|
229
|
+
def get(self, location: str, **kwargs: Any) -> bytes:
|
|
230
|
+
return b"data"
|
|
231
|
+
|
|
232
|
+
def put(self, data: bytes, location: str, **kwargs: Any) -> ConnectRef:
|
|
233
|
+
return ConnectRef(location=location)
|
|
234
|
+
|
|
235
|
+
instance = InvalidConnect()
|
|
236
|
+
assert not isinstance(instance, ConnectLike)
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
# =============================================================================
|
|
240
|
+
# Type Checking Tests
|
|
241
|
+
# =============================================================================
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def test_isinstance_with_valid_class():
|
|
245
|
+
"""测试 isinstance 检查有效类"""
|
|
246
|
+
|
|
247
|
+
class MyConnect:
|
|
248
|
+
def __init__(self):
|
|
249
|
+
self.kind = "test"
|
|
250
|
+
|
|
251
|
+
def get_object(self, location: str, **kwargs: Any) -> bytes:
|
|
252
|
+
return b""
|
|
253
|
+
|
|
254
|
+
def put_object(self, data: bytes, location: str, **kwargs: Any) -> ConnectRef:
|
|
255
|
+
return ConnectRef(location=location)
|
|
256
|
+
|
|
257
|
+
assert isinstance(MyConnect(), ConnectLike)
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def test_isinstance_with_invalid_class():
|
|
261
|
+
"""测试 isinstance 检查无效类"""
|
|
262
|
+
|
|
263
|
+
class NotConnect:
|
|
264
|
+
def __init__(self):
|
|
265
|
+
self.kind = "test"
|
|
266
|
+
|
|
267
|
+
def other_method(self):
|
|
268
|
+
pass
|
|
269
|
+
|
|
270
|
+
assert not isinstance(NotConnect(), ConnectLike)
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
def test_isinstance_with_builtin_types():
|
|
274
|
+
"""测试 isinstance 检查内置类型"""
|
|
275
|
+
assert not isinstance(str(), ConnectLike)
|
|
276
|
+
assert not isinstance(int(), ConnectLike)
|
|
277
|
+
assert not isinstance(list(), ConnectLike)
|
|
278
|
+
assert not isinstance(dict(), ConnectLike)
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
def test_isinstance_with_none():
|
|
282
|
+
"""测试 isinstance 检查 None"""
|
|
283
|
+
assert not isinstance(None, ConnectLike)
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
# =============================================================================
|
|
287
|
+
# Inheritance Tests
|
|
288
|
+
# =============================================================================
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
def test_can_inherit_from_protocol():
|
|
292
|
+
"""测试可以继承协议"""
|
|
293
|
+
|
|
294
|
+
class BaseConnect(ConnectLike):
|
|
295
|
+
def __init__(self):
|
|
296
|
+
self.kind = "test"
|
|
297
|
+
|
|
298
|
+
def get_object(self, location: str, **kwargs: Any) -> bytes:
|
|
299
|
+
return b"base"
|
|
300
|
+
|
|
301
|
+
def put_object(self, data: bytes, location: str, **kwargs: Any) -> ConnectRef:
|
|
302
|
+
return ConnectRef(location=location)
|
|
303
|
+
|
|
304
|
+
instance = BaseConnect()
|
|
305
|
+
assert isinstance(instance, ConnectLike)
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
def test_inherited_class_can_override():
|
|
309
|
+
"""测试继承类可以重写方法"""
|
|
310
|
+
|
|
311
|
+
class BaseConnect(ConnectLike):
|
|
312
|
+
def __init__(self):
|
|
313
|
+
self.kind = "test"
|
|
314
|
+
|
|
315
|
+
def get_object(self, location: str, **kwargs: Any) -> bytes:
|
|
316
|
+
return b"base"
|
|
317
|
+
|
|
318
|
+
def put_object(self, data: bytes, location: str, **kwargs: Any) -> ConnectRef:
|
|
319
|
+
return ConnectRef(location=location, kind="base")
|
|
320
|
+
|
|
321
|
+
class DerivedConnect(BaseConnect):
|
|
322
|
+
def __init__(self):
|
|
323
|
+
self.kind = "test"
|
|
324
|
+
|
|
325
|
+
def get_object(self, location: str, **kwargs: Any) -> bytes:
|
|
326
|
+
return b"derived"
|
|
327
|
+
|
|
328
|
+
def put_object(self, data: bytes, location: str, **kwargs: Any) -> ConnectRef:
|
|
329
|
+
return ConnectRef(location=location, kind="derived")
|
|
330
|
+
|
|
331
|
+
derived = DerivedConnect()
|
|
332
|
+
assert isinstance(derived, ConnectLike)
|
|
333
|
+
assert derived.get_object("loc") == b"derived"
|
|
334
|
+
assert derived.put_object(b"data", "loc").kind == "derived"
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
# =============================================================================
|
|
338
|
+
# Real-World Usage Tests
|
|
339
|
+
# =============================================================================
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
def test_s3_like_implementation():
|
|
343
|
+
"""测试类似 S3 的实现"""
|
|
344
|
+
|
|
345
|
+
class S3MockConnect:
|
|
346
|
+
def __init__(self):
|
|
347
|
+
self.kind = "test"
|
|
348
|
+
self.storage = {}
|
|
349
|
+
|
|
350
|
+
def get_object(self, location: str, **kwargs: Any) -> bytes:
|
|
351
|
+
if location not in self.storage:
|
|
352
|
+
raise KeyError(f"Object not found: {location}")
|
|
353
|
+
return self.storage[location]
|
|
354
|
+
|
|
355
|
+
def put_object(self, data: bytes, location: str, **kwargs: Any) -> ConnectRef:
|
|
356
|
+
self.storage[location] = data
|
|
357
|
+
return ConnectRef(location=location, kind="s3")
|
|
358
|
+
|
|
359
|
+
connect = S3MockConnect()
|
|
360
|
+
assert isinstance(connect, ConnectLike)
|
|
361
|
+
|
|
362
|
+
# Put and get
|
|
363
|
+
ref = connect.put_object(b"test_data", "s3://bucket/key")
|
|
364
|
+
assert ref.location == "s3://bucket/key"
|
|
365
|
+
|
|
366
|
+
data = connect.get_object("s3://bucket/key")
|
|
367
|
+
assert data == b"test_data"
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
def test_filesystem_like_implementation():
|
|
371
|
+
"""测试类似文件系统的实现"""
|
|
372
|
+
|
|
373
|
+
class FileSystemConnect:
|
|
374
|
+
def __init__(self):
|
|
375
|
+
self.kind = "test"
|
|
376
|
+
self.files = {}
|
|
377
|
+
|
|
378
|
+
def get_object(self, location: str, **kwargs: Any) -> bytes:
|
|
379
|
+
return self.files.get(location, b"")
|
|
380
|
+
|
|
381
|
+
def put_object(self, data: bytes, location: str, **kwargs: Any) -> ConnectRef:
|
|
382
|
+
self.files[location] = data
|
|
383
|
+
return ConnectRef(location=location, kind="filesystem")
|
|
384
|
+
|
|
385
|
+
connect = FileSystemConnect()
|
|
386
|
+
assert isinstance(connect, ConnectLike)
|
|
387
|
+
|
|
388
|
+
# Test operations
|
|
389
|
+
ref = connect.put_object(b"file_content", "/path/to/file")
|
|
390
|
+
assert ref.kind == "filesystem"
|
|
391
|
+
|
|
392
|
+
content = connect.get_object("/path/to/file")
|
|
393
|
+
assert content == b"file_content"
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
def test_error_handling_implementation():
|
|
397
|
+
"""测试包含错误处理的实现"""
|
|
398
|
+
|
|
399
|
+
class ErrorHandlingConnect:
|
|
400
|
+
def __init__(self):
|
|
401
|
+
self.kind = "test"
|
|
402
|
+
|
|
403
|
+
def get_object(self, location: str, **kwargs: Any) -> bytes:
|
|
404
|
+
if not location:
|
|
405
|
+
raise ValueError("Location cannot be empty")
|
|
406
|
+
return b"data"
|
|
407
|
+
|
|
408
|
+
def put_object(self, data: bytes, location: str, **kwargs: Any) -> ConnectRef:
|
|
409
|
+
if not data:
|
|
410
|
+
raise ValueError("Data cannot be empty")
|
|
411
|
+
if not location:
|
|
412
|
+
raise ValueError("Location cannot be empty")
|
|
413
|
+
return ConnectRef(location=location)
|
|
414
|
+
|
|
415
|
+
connect = ErrorHandlingConnect()
|
|
416
|
+
assert isinstance(connect, ConnectLike)
|
|
417
|
+
|
|
418
|
+
# Test error cases
|
|
419
|
+
with pytest.raises(ValueError, match="Location cannot be empty"):
|
|
420
|
+
connect.get_object("")
|
|
421
|
+
|
|
422
|
+
with pytest.raises(ValueError, match="Data cannot be empty"):
|
|
423
|
+
connect.put_object(b"", "location")
|
|
424
|
+
|
|
425
|
+
|
|
426
|
+
def test_async_style_implementation():
|
|
427
|
+
"""测试异步风格的实现(同步模拟)"""
|
|
428
|
+
|
|
429
|
+
class AsyncStyleConnect:
|
|
430
|
+
def __init__(self):
|
|
431
|
+
self.kind = "test"
|
|
432
|
+
self.pending = []
|
|
433
|
+
|
|
434
|
+
def get_object(self, location: str, **kwargs: Any) -> bytes:
|
|
435
|
+
# Simulate async operation
|
|
436
|
+
self.pending.append(("get", location))
|
|
437
|
+
return b"async_data"
|
|
438
|
+
|
|
439
|
+
def put_object(self, data: bytes, location: str, **kwargs: Any) -> ConnectRef:
|
|
440
|
+
# Simulate async operation
|
|
441
|
+
self.pending.append(("put", location))
|
|
442
|
+
return ConnectRef(location=location, kind="async")
|
|
443
|
+
|
|
444
|
+
connect = AsyncStyleConnect()
|
|
445
|
+
assert isinstance(connect, ConnectLike)
|
|
446
|
+
|
|
447
|
+
connect.get_object("loc1")
|
|
448
|
+
connect.put_object(b"data", "loc2")
|
|
449
|
+
|
|
450
|
+
assert len(connect.pending) == 2
|
|
451
|
+
assert connect.pending[0] == ("get", "loc1")
|
|
452
|
+
assert connect.pending[1] == ("put", "loc2")
|
|
453
|
+
|
|
454
|
+
|
|
455
|
+
# =============================================================================
|
|
456
|
+
# Protocol Attribute Tests
|
|
457
|
+
# =============================================================================
|
|
458
|
+
|
|
459
|
+
|
|
460
|
+
def test_protocol_has_correct_attributes():
|
|
461
|
+
"""测试协议具有正确的属性"""
|
|
462
|
+
# Check protocol defines required methods
|
|
463
|
+
assert hasattr(ConnectLike, "get_object")
|
|
464
|
+
assert hasattr(ConnectLike, "put_object")
|
|
465
|
+
# Verify it's marked as a protocol
|
|
466
|
+
assert hasattr(ConnectLike, "_is_protocol") or hasattr(ConnectLike, "__protocol_attrs__")
|
|
467
|
+
|
|
468
|
+
|
|
469
|
+
def test_protocol_module():
|
|
470
|
+
"""测试协议所属模块"""
|
|
471
|
+
assert ConnectLike.__module__ == "xfintech.connect.common.connect"
|
|
472
|
+
|
|
473
|
+
|
|
474
|
+
def test_protocol_name():
|
|
475
|
+
"""测试协议名称"""
|
|
476
|
+
assert ConnectLike.__name__ == "ConnectLike"
|
|
477
|
+
|
|
478
|
+
|
|
479
|
+
def test_protocol_has_docstring():
|
|
480
|
+
"""测试协议有文档字符串"""
|
|
481
|
+
assert ConnectLike.__doc__ is not None
|
|
482
|
+
assert len(ConnectLike.__doc__) > 0
|
|
483
|
+
|
|
484
|
+
|
|
485
|
+
# =============================================================================
|
|
486
|
+
# Edge Cases
|
|
487
|
+
# =============================================================================
|
|
488
|
+
|
|
489
|
+
|
|
490
|
+
def test_implementation_with_optional_parameters():
|
|
491
|
+
"""测试带可选参数的实现"""
|
|
492
|
+
|
|
493
|
+
class OptionalConnect:
|
|
494
|
+
def get_object(self, location: str, **kwargs: Any) -> bytes:
|
|
495
|
+
return b"data"
|
|
496
|
+
|
|
497
|
+
def put_object(self, data: bytes, location: str, **kwargs: Any) -> ConnectRef:
|
|
498
|
+
return ConnectRef(
|
|
499
|
+
location=location,
|
|
500
|
+
kind=kwargs.get("kind"),
|
|
501
|
+
)
|
|
502
|
+
|
|
503
|
+
connect = OptionalConnect()
|
|
504
|
+
assert isinstance(connect, ConnectLike)
|
|
505
|
+
|
|
506
|
+
ref = connect.put_object(b"data", "loc", kind="custom")
|
|
507
|
+
assert ref.kind == "custom"
|
|
508
|
+
|
|
509
|
+
|
|
510
|
+
def test_implementation_with_default_values():
|
|
511
|
+
"""测试带默认值的实现"""
|
|
512
|
+
|
|
513
|
+
class DefaultConnect:
|
|
514
|
+
def get_object(self, location: str = "default", **kwargs: Any) -> bytes:
|
|
515
|
+
return location.encode()
|
|
516
|
+
|
|
517
|
+
def put_object(self, data: bytes = b"", location: str = "default", **kwargs: Any) -> ConnectRef:
|
|
518
|
+
return ConnectRef(location=location)
|
|
519
|
+
|
|
520
|
+
connect = DefaultConnect()
|
|
521
|
+
assert isinstance(connect, ConnectLike)
|
|
522
|
+
|
|
523
|
+
|
|
524
|
+
def test_multiple_instances():
|
|
525
|
+
"""测试多个实例"""
|
|
526
|
+
|
|
527
|
+
class SimpleConnect:
|
|
528
|
+
def __init__(self, name: str):
|
|
529
|
+
self.name = name
|
|
530
|
+
|
|
531
|
+
def get_object(self, location: str, **kwargs: Any) -> bytes:
|
|
532
|
+
return self.name.encode()
|
|
533
|
+
|
|
534
|
+
def put_object(self, data: bytes, location: str, **kwargs: Any) -> ConnectRef:
|
|
535
|
+
return ConnectRef(location=location, kind=self.name)
|
|
536
|
+
|
|
537
|
+
connect1 = SimpleConnect("first")
|
|
538
|
+
connect2 = SimpleConnect("second")
|
|
539
|
+
|
|
540
|
+
assert isinstance(connect1, ConnectLike)
|
|
541
|
+
assert isinstance(connect2, ConnectLike)
|
|
542
|
+
|
|
543
|
+
assert connect1.get_object("loc") == b"first"
|
|
544
|
+
assert connect2.get_object("loc") == b"second"
|