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,524 @@
|
|
|
1
|
+
"""
|
|
2
|
+
描述:
|
|
3
|
+
- xfintech.connect.common.error 模块的单元测试。
|
|
4
|
+
- 测试所有自定义异常类的继承、实例化和使用。
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import pytest
|
|
10
|
+
|
|
11
|
+
from xfintech.connect.common.error import (
|
|
12
|
+
ConnectFailedError,
|
|
13
|
+
ConnectKeyError,
|
|
14
|
+
ConnectRefKeyError,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
# =============================================================================
|
|
18
|
+
# ConnectRefKeyError Tests
|
|
19
|
+
# =============================================================================
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def test_connectrefkeyerror_inherits_from_keyerror():
|
|
23
|
+
"""测试 ConnectRefKeyError 继承自 KeyError"""
|
|
24
|
+
assert issubclass(ConnectRefKeyError, KeyError)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def test_connectrefkeyerror_can_instantiate():
|
|
28
|
+
"""测试可以实例化 ConnectRefKeyError"""
|
|
29
|
+
error = ConnectRefKeyError("Test message")
|
|
30
|
+
assert isinstance(error, ConnectRefKeyError)
|
|
31
|
+
assert isinstance(error, KeyError)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def test_connectrefkeyerror_can_raise():
|
|
35
|
+
"""测试可以抛出 ConnectRefKeyError"""
|
|
36
|
+
with pytest.raises(ConnectRefKeyError):
|
|
37
|
+
raise ConnectRefKeyError("Missing required key")
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def test_connectrefkeyerror_message():
|
|
41
|
+
"""测试 ConnectRefKeyError 消息"""
|
|
42
|
+
msg = "'location' is required in data"
|
|
43
|
+
error = ConnectRefKeyError(msg)
|
|
44
|
+
# KeyError wraps message in quotes
|
|
45
|
+
assert msg in str(error)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def test_connectrefkeyerror_can_catch_as_keyerror():
|
|
49
|
+
"""测试可以作为 KeyError 捕获"""
|
|
50
|
+
with pytest.raises(KeyError):
|
|
51
|
+
raise ConnectRefKeyError("Missing key")
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def test_connectrefkeyerror_empty_message():
|
|
55
|
+
"""测试空消息"""
|
|
56
|
+
error = ConnectRefKeyError()
|
|
57
|
+
assert isinstance(error, ConnectRefKeyError)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def test_connectrefkeyerror_with_multiple_args():
|
|
61
|
+
"""测试多参数实例化"""
|
|
62
|
+
error = ConnectRefKeyError("Error", "Extra info")
|
|
63
|
+
assert len(error.args) == 2
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def test_connectrefkeyerror_use_case():
|
|
67
|
+
"""测试实际使用场景"""
|
|
68
|
+
|
|
69
|
+
def validate_data(data):
|
|
70
|
+
if "location" not in data:
|
|
71
|
+
raise ConnectRefKeyError("'location' is required in data")
|
|
72
|
+
|
|
73
|
+
with pytest.raises(ConnectRefKeyError) as excinfo:
|
|
74
|
+
validate_data({"kind": "s3"})
|
|
75
|
+
assert "'location' is required in data" in str(excinfo.value)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def test_connectrefkeyerror_in_dict_access():
|
|
79
|
+
"""测试在字典访问中使用"""
|
|
80
|
+
|
|
81
|
+
def get_location(data):
|
|
82
|
+
try:
|
|
83
|
+
return data["location"]
|
|
84
|
+
except KeyError:
|
|
85
|
+
raise ConnectRefKeyError("location key missing")
|
|
86
|
+
|
|
87
|
+
with pytest.raises(ConnectRefKeyError):
|
|
88
|
+
get_location({})
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def test_connectrefkeyerror_with_cause():
|
|
92
|
+
"""测试带原因链的异常"""
|
|
93
|
+
original = KeyError("original")
|
|
94
|
+
try:
|
|
95
|
+
raise ConnectRefKeyError("wrapped") from original
|
|
96
|
+
except ConnectRefKeyError as e:
|
|
97
|
+
assert e.__cause__ is original
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
# =============================================================================
|
|
101
|
+
# ConnectKeyError Tests
|
|
102
|
+
# =============================================================================
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def test_connectkeyerror_inherits_from_keyerror():
|
|
106
|
+
"""测试 ConnectKeyError 继承自 KeyError"""
|
|
107
|
+
assert issubclass(ConnectKeyError, KeyError)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def test_connectkeyerror_can_instantiate():
|
|
111
|
+
"""测试可以实例化 ConnectKeyError"""
|
|
112
|
+
error = ConnectKeyError("Test message")
|
|
113
|
+
assert isinstance(error, ConnectKeyError)
|
|
114
|
+
assert isinstance(error, KeyError)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def test_connectkeyerror_can_raise():
|
|
118
|
+
"""测试可以抛出 ConnectKeyError"""
|
|
119
|
+
with pytest.raises(ConnectKeyError):
|
|
120
|
+
raise ConnectKeyError("Invalid key")
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def test_connectkeyerror_message():
|
|
124
|
+
"""测试 ConnectKeyError 消息"""
|
|
125
|
+
msg = "location cannot be None"
|
|
126
|
+
error = ConnectKeyError(msg)
|
|
127
|
+
assert msg in str(error)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def test_connectkeyerror_can_catch_as_keyerror():
|
|
131
|
+
"""测试可以作为 KeyError 捕获"""
|
|
132
|
+
with pytest.raises(KeyError):
|
|
133
|
+
raise ConnectKeyError("Key error")
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def test_connectkeyerror_empty_message():
|
|
137
|
+
"""测试空消息"""
|
|
138
|
+
error = ConnectKeyError()
|
|
139
|
+
assert isinstance(error, ConnectKeyError)
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def test_connectkeyerror_with_multiple_args():
|
|
143
|
+
"""测试多参数实例化"""
|
|
144
|
+
error = ConnectKeyError("Error", "Extra")
|
|
145
|
+
assert len(error.args) == 2
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def test_connectkeyerror_use_case():
|
|
149
|
+
"""测试实际使用场景"""
|
|
150
|
+
|
|
151
|
+
def validate_location(location):
|
|
152
|
+
if location is None:
|
|
153
|
+
raise ConnectKeyError("location cannot be None")
|
|
154
|
+
if not location:
|
|
155
|
+
raise ConnectKeyError("location cannot be empty")
|
|
156
|
+
|
|
157
|
+
with pytest.raises(ConnectKeyError) as excinfo:
|
|
158
|
+
validate_location(None)
|
|
159
|
+
assert "cannot be None" in str(excinfo.value)
|
|
160
|
+
|
|
161
|
+
with pytest.raises(ConnectKeyError) as excinfo:
|
|
162
|
+
validate_location("")
|
|
163
|
+
assert "cannot be empty" in str(excinfo.value)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def test_connectkeyerror_in_validation():
|
|
167
|
+
"""测试在验证逻辑中使用"""
|
|
168
|
+
|
|
169
|
+
def check_required_keys(data, required):
|
|
170
|
+
for key in required:
|
|
171
|
+
if key not in data:
|
|
172
|
+
raise ConnectKeyError(f"Required key missing: {key}")
|
|
173
|
+
|
|
174
|
+
with pytest.raises(ConnectKeyError, match="location"):
|
|
175
|
+
check_required_keys({"kind": "s3"}, ["location", "kind"])
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def test_connectkeyerror_with_cause():
|
|
179
|
+
"""测试带原因链的异常"""
|
|
180
|
+
original = ValueError("Invalid value")
|
|
181
|
+
try:
|
|
182
|
+
raise ConnectKeyError("Key invalid") from original
|
|
183
|
+
except ConnectKeyError as e:
|
|
184
|
+
assert e.__cause__ is original
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
# =============================================================================
|
|
188
|
+
# ConnectFailedError Tests
|
|
189
|
+
# =============================================================================
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def test_connectfailederror_inherits_from_exception():
|
|
193
|
+
"""测试 ConnectFailedError 继承自 Exception"""
|
|
194
|
+
assert issubclass(ConnectFailedError, Exception)
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def test_connectfailederror_not_keyerror():
|
|
198
|
+
"""测试不是 KeyError"""
|
|
199
|
+
assert not issubclass(ConnectFailedError, KeyError)
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def test_connectfailederror_can_instantiate():
|
|
203
|
+
"""测试可以实例化 ConnectFailedError"""
|
|
204
|
+
error = ConnectFailedError("Test message")
|
|
205
|
+
assert isinstance(error, ConnectFailedError)
|
|
206
|
+
assert isinstance(error, Exception)
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def test_connectfailederror_can_raise():
|
|
210
|
+
"""测试可以抛出 ConnectFailedError"""
|
|
211
|
+
with pytest.raises(ConnectFailedError):
|
|
212
|
+
raise ConnectFailedError("Operation failed")
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def test_connectfailederror_message():
|
|
216
|
+
"""测试 ConnectFailedError 消息"""
|
|
217
|
+
msg = "Failed to read file"
|
|
218
|
+
error = ConnectFailedError(msg)
|
|
219
|
+
assert str(error) == msg
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def test_connectfailederror_can_catch_as_exception():
|
|
223
|
+
"""测试可以作为 Exception 捕获"""
|
|
224
|
+
with pytest.raises(Exception):
|
|
225
|
+
raise ConnectFailedError("Failed")
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def test_connectfailederror_empty_message():
|
|
229
|
+
"""测试空消息"""
|
|
230
|
+
error = ConnectFailedError()
|
|
231
|
+
assert isinstance(error, ConnectFailedError)
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def test_connectfailederror_with_multiple_args():
|
|
235
|
+
"""测试多参数实例化"""
|
|
236
|
+
error = ConnectFailedError("Error", "Details")
|
|
237
|
+
assert len(error.args) == 2
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def test_connectfailederror_use_case_read():
|
|
241
|
+
"""测试读取失败场景"""
|
|
242
|
+
|
|
243
|
+
def read_file(path):
|
|
244
|
+
try:
|
|
245
|
+
with open(path, "rb") as f:
|
|
246
|
+
return f.read()
|
|
247
|
+
except Exception as e:
|
|
248
|
+
raise ConnectFailedError(f"Failed to read {path}: {e}") from e
|
|
249
|
+
|
|
250
|
+
with pytest.raises(ConnectFailedError) as excinfo:
|
|
251
|
+
read_file("/nonexistent/file.txt")
|
|
252
|
+
assert "Failed to read" in str(excinfo.value)
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
def test_connectfailederror_use_case_write():
|
|
256
|
+
"""测试写入失败场景"""
|
|
257
|
+
|
|
258
|
+
def write_file(path, data):
|
|
259
|
+
try:
|
|
260
|
+
with open(path, "wb") as f:
|
|
261
|
+
f.write(data)
|
|
262
|
+
except Exception as e:
|
|
263
|
+
raise ConnectFailedError(f"Failed to write {path}: {e}") from e
|
|
264
|
+
|
|
265
|
+
with pytest.raises(ConnectFailedError) as excinfo:
|
|
266
|
+
write_file("/invalid/path/file.txt", b"data")
|
|
267
|
+
assert "Failed to write" in str(excinfo.value)
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
def test_connectfailederror_with_cause():
|
|
271
|
+
"""测试带原因链的异常"""
|
|
272
|
+
original = IOError("File not found")
|
|
273
|
+
try:
|
|
274
|
+
raise ConnectFailedError("Operation failed") from original
|
|
275
|
+
except ConnectFailedError as e:
|
|
276
|
+
assert e.__cause__ is original
|
|
277
|
+
assert isinstance(e.__cause__, IOError)
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
def test_connectfailederror_nested_exception():
|
|
281
|
+
"""测试嵌套异常"""
|
|
282
|
+
try:
|
|
283
|
+
try:
|
|
284
|
+
raise ValueError("Inner error")
|
|
285
|
+
except ValueError as ve:
|
|
286
|
+
raise ConnectFailedError("Outer error") from ve
|
|
287
|
+
except ConnectFailedError as e:
|
|
288
|
+
assert isinstance(e.__cause__, ValueError)
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
# =============================================================================
|
|
292
|
+
# Cross-Error Tests
|
|
293
|
+
# =============================================================================
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
def test_all_errors_are_distinct():
|
|
297
|
+
"""测试所有错误类是不同的"""
|
|
298
|
+
assert ConnectRefKeyError is not ConnectKeyError
|
|
299
|
+
assert ConnectRefKeyError is not ConnectFailedError
|
|
300
|
+
assert ConnectKeyError is not ConnectFailedError
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
def test_error_hierarchy():
|
|
304
|
+
"""测试错误层次结构"""
|
|
305
|
+
# Both KeyError subclasses
|
|
306
|
+
assert issubclass(ConnectRefKeyError, KeyError)
|
|
307
|
+
assert issubclass(ConnectKeyError, KeyError)
|
|
308
|
+
|
|
309
|
+
# But not subclasses of each other
|
|
310
|
+
assert not issubclass(ConnectRefKeyError, ConnectKeyError)
|
|
311
|
+
assert not issubclass(ConnectKeyError, ConnectRefKeyError)
|
|
312
|
+
|
|
313
|
+
# ConnectFailedError is separate
|
|
314
|
+
assert not issubclass(ConnectFailedError, KeyError)
|
|
315
|
+
assert not issubclass(ConnectFailedError, ConnectRefKeyError)
|
|
316
|
+
assert not issubclass(ConnectFailedError, ConnectKeyError)
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
def test_can_catch_multiple_keyerrors():
|
|
320
|
+
"""测试可以捕获多个 KeyError 子类"""
|
|
321
|
+
|
|
322
|
+
def raise_ref_key_error():
|
|
323
|
+
raise ConnectRefKeyError("ref error")
|
|
324
|
+
|
|
325
|
+
def raise_connect_key_error():
|
|
326
|
+
raise ConnectKeyError("connect error")
|
|
327
|
+
|
|
328
|
+
# Can catch both as KeyError
|
|
329
|
+
with pytest.raises(KeyError):
|
|
330
|
+
raise_ref_key_error()
|
|
331
|
+
|
|
332
|
+
with pytest.raises(KeyError):
|
|
333
|
+
raise_connect_key_error()
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
def test_specific_error_catching():
|
|
337
|
+
"""测试特定错误捕获"""
|
|
338
|
+
|
|
339
|
+
def operation(error_type):
|
|
340
|
+
if error_type == "ref":
|
|
341
|
+
raise ConnectRefKeyError("ref error")
|
|
342
|
+
elif error_type == "key":
|
|
343
|
+
raise ConnectKeyError("key error")
|
|
344
|
+
else:
|
|
345
|
+
raise ConnectFailedError("failed")
|
|
346
|
+
|
|
347
|
+
# Catch specific errors
|
|
348
|
+
with pytest.raises(ConnectRefKeyError):
|
|
349
|
+
operation("ref")
|
|
350
|
+
|
|
351
|
+
with pytest.raises(ConnectKeyError):
|
|
352
|
+
operation("key")
|
|
353
|
+
|
|
354
|
+
with pytest.raises(ConnectFailedError):
|
|
355
|
+
operation("failed")
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
def test_error_catching_order():
|
|
359
|
+
"""测试错误捕获顺序"""
|
|
360
|
+
|
|
361
|
+
def risky_operation():
|
|
362
|
+
raise ConnectRefKeyError("error")
|
|
363
|
+
|
|
364
|
+
# Specific catches first
|
|
365
|
+
caught_specific = False
|
|
366
|
+
caught_general = False
|
|
367
|
+
|
|
368
|
+
try:
|
|
369
|
+
risky_operation()
|
|
370
|
+
except ConnectRefKeyError:
|
|
371
|
+
caught_specific = True
|
|
372
|
+
except KeyError:
|
|
373
|
+
caught_general = True
|
|
374
|
+
|
|
375
|
+
assert caught_specific
|
|
376
|
+
assert not caught_general
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
# =============================================================================
|
|
380
|
+
# Error Message Tests
|
|
381
|
+
# =============================================================================
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
def test_error_messages_are_preserved():
|
|
385
|
+
"""测试错误消息被保留"""
|
|
386
|
+
messages = [
|
|
387
|
+
"Simple message",
|
|
388
|
+
"Message with 'quotes'",
|
|
389
|
+
"Unicode message: 测试错误",
|
|
390
|
+
"Long " + "x" * 1000 + " message",
|
|
391
|
+
]
|
|
392
|
+
|
|
393
|
+
for msg in messages:
|
|
394
|
+
e1 = ConnectRefKeyError(msg)
|
|
395
|
+
e2 = ConnectKeyError(msg)
|
|
396
|
+
e3 = ConnectFailedError(msg)
|
|
397
|
+
|
|
398
|
+
# For KeyError subclasses, message might be wrapped
|
|
399
|
+
assert msg in str(e1) or msg in repr(e1) or msg.replace("'", "\\'") in str(e1)
|
|
400
|
+
assert msg in str(e2) or msg in repr(e2) or msg.replace("'", "\\'") in str(e2)
|
|
401
|
+
assert msg in str(e3)
|
|
402
|
+
|
|
403
|
+
|
|
404
|
+
def test_formatted_error_messages():
|
|
405
|
+
"""测试格式化的错误消息"""
|
|
406
|
+
location = "/path/to/file"
|
|
407
|
+
error = ConnectFailedError(f"Failed to access {location}")
|
|
408
|
+
assert location in str(error)
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
# =============================================================================
|
|
412
|
+
# Practical Workflow Tests
|
|
413
|
+
# =============================================================================
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
def test_connect_workflow_errors():
|
|
417
|
+
"""测试连接工作流中的错误处理"""
|
|
418
|
+
|
|
419
|
+
def connect_operation(location):
|
|
420
|
+
# Validation
|
|
421
|
+
if not location:
|
|
422
|
+
raise ConnectKeyError("location is required")
|
|
423
|
+
|
|
424
|
+
# Try operation
|
|
425
|
+
try:
|
|
426
|
+
# Simulate operation
|
|
427
|
+
if location == "invalid":
|
|
428
|
+
raise IOError("Cannot access")
|
|
429
|
+
except IOError as e:
|
|
430
|
+
raise ConnectFailedError(f"Operation failed: {e}") from e
|
|
431
|
+
|
|
432
|
+
# Test validation error
|
|
433
|
+
with pytest.raises(ConnectKeyError):
|
|
434
|
+
connect_operation("")
|
|
435
|
+
|
|
436
|
+
# Test operation error
|
|
437
|
+
with pytest.raises(ConnectFailedError):
|
|
438
|
+
connect_operation("invalid")
|
|
439
|
+
|
|
440
|
+
|
|
441
|
+
def test_ref_creation_workflow_errors():
|
|
442
|
+
"""测试引用创建工作流中的错误处理"""
|
|
443
|
+
|
|
444
|
+
def create_ref_from_data(data):
|
|
445
|
+
if "location" not in data:
|
|
446
|
+
raise ConnectRefKeyError("'location' is required")
|
|
447
|
+
|
|
448
|
+
location = data["location"]
|
|
449
|
+
if not location:
|
|
450
|
+
raise ConnectKeyError("location cannot be empty")
|
|
451
|
+
|
|
452
|
+
# Simulate ref creation succeeds
|
|
453
|
+
return {"location": location, "kind": data.get("kind")}
|
|
454
|
+
|
|
455
|
+
# Test missing location
|
|
456
|
+
with pytest.raises(ConnectRefKeyError):
|
|
457
|
+
create_ref_from_data({"kind": "s3"})
|
|
458
|
+
|
|
459
|
+
# Test empty location
|
|
460
|
+
with pytest.raises(ConnectKeyError):
|
|
461
|
+
create_ref_from_data({"location": ""})
|
|
462
|
+
|
|
463
|
+
# Test success
|
|
464
|
+
result = create_ref_from_data({"location": "test"})
|
|
465
|
+
assert result["location"] == "test"
|
|
466
|
+
|
|
467
|
+
|
|
468
|
+
def test_error_context_preservation():
|
|
469
|
+
"""测试错误上下文保留"""
|
|
470
|
+
|
|
471
|
+
def inner_operation():
|
|
472
|
+
raise ValueError("Inner error with details")
|
|
473
|
+
|
|
474
|
+
def outer_operation():
|
|
475
|
+
try:
|
|
476
|
+
inner_operation()
|
|
477
|
+
except ValueError as e:
|
|
478
|
+
raise ConnectFailedError("Outer operation failed") from e
|
|
479
|
+
|
|
480
|
+
with pytest.raises(ConnectFailedError) as excinfo:
|
|
481
|
+
outer_operation()
|
|
482
|
+
|
|
483
|
+
error = excinfo.value
|
|
484
|
+
assert error.__cause__ is not None
|
|
485
|
+
assert isinstance(error.__cause__, ValueError)
|
|
486
|
+
assert "Inner error" in str(error.__cause__)
|
|
487
|
+
|
|
488
|
+
|
|
489
|
+
# =============================================================================
|
|
490
|
+
# Error Module Tests
|
|
491
|
+
# =============================================================================
|
|
492
|
+
|
|
493
|
+
|
|
494
|
+
def test_all_errors_importable():
|
|
495
|
+
"""测试所有错误可以导入"""
|
|
496
|
+
from xfintech.connect.common.error import (
|
|
497
|
+
ConnectFailedError,
|
|
498
|
+
ConnectKeyError,
|
|
499
|
+
ConnectRefKeyError,
|
|
500
|
+
)
|
|
501
|
+
|
|
502
|
+
assert ConnectRefKeyError is not None
|
|
503
|
+
assert ConnectKeyError is not None
|
|
504
|
+
assert ConnectFailedError is not None
|
|
505
|
+
|
|
506
|
+
|
|
507
|
+
def test_error_module_has_all_classes():
|
|
508
|
+
"""测试错误模块包含所有类"""
|
|
509
|
+
import xfintech.connect.common.error as error_module
|
|
510
|
+
|
|
511
|
+
assert hasattr(error_module, "ConnectRefKeyError")
|
|
512
|
+
assert hasattr(error_module, "ConnectKeyError")
|
|
513
|
+
assert hasattr(error_module, "ConnectFailedError")
|
|
514
|
+
|
|
515
|
+
|
|
516
|
+
def test_errors_have_docstrings():
|
|
517
|
+
"""测试错误类有文档字符串"""
|
|
518
|
+
assert ConnectRefKeyError.__doc__ is not None
|
|
519
|
+
assert ConnectKeyError.__doc__ is not None
|
|
520
|
+
assert ConnectFailedError.__doc__ is not None
|
|
521
|
+
|
|
522
|
+
assert len(ConnectRefKeyError.__doc__) > 0
|
|
523
|
+
assert len(ConnectKeyError.__doc__) > 0
|
|
524
|
+
assert len(ConnectFailedError.__doc__) > 0
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Any, Union
|
|
5
|
+
|
|
6
|
+
from xfintech.connect.common.connect import ConnectLike
|
|
7
|
+
from xfintech.connect.common.connectref import ConnectRef
|
|
8
|
+
from xfintech.connect.common.error import ConnectFailedError, ConnectKeyError
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class MacOSConnect(ConnectLike):
|
|
12
|
+
"""
|
|
13
|
+
描述:
|
|
14
|
+
- macOS 本地文件系统连接实现类。
|
|
15
|
+
- 实现了 ConnectLike 协议,用于与本地文件系统进行交互。
|
|
16
|
+
- 支持读取和写入本地文件。
|
|
17
|
+
- 自动处理路径解析、用户目录展开和父目录创建。
|
|
18
|
+
- 适用于 macOS 和其他 Unix-like 系统。
|
|
19
|
+
|
|
20
|
+
属性:
|
|
21
|
+
- kind: 连接类型标识,固定为 "macos"。
|
|
22
|
+
|
|
23
|
+
例子:
|
|
24
|
+
```python
|
|
25
|
+
from xfintech.connect.instance.macos import MacOSConnect
|
|
26
|
+
|
|
27
|
+
connect = MacOSConnect()
|
|
28
|
+
|
|
29
|
+
# 写入数据到本地文件
|
|
30
|
+
ref = connect.put_object(b"data", "/path/to/file.txt")
|
|
31
|
+
|
|
32
|
+
# 读取本地文件
|
|
33
|
+
data = connect.get_object("/path/to/file.txt")
|
|
34
|
+
|
|
35
|
+
# 使用 Path 对象
|
|
36
|
+
from pathlib import Path
|
|
37
|
+
ref = connect.put_object(b"data", Path("~/data/file.txt"))
|
|
38
|
+
|
|
39
|
+
# 支持用户目录展开
|
|
40
|
+
data = connect.get_object("~/documents/data.bin")
|
|
41
|
+
```
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
def __init__(self) -> None:
|
|
45
|
+
self.kind: str = "macos"
|
|
46
|
+
|
|
47
|
+
def _resolve_path(
|
|
48
|
+
self,
|
|
49
|
+
location: Union[str, Path],
|
|
50
|
+
) -> Path:
|
|
51
|
+
if location is None:
|
|
52
|
+
msg = "location cannot be None"
|
|
53
|
+
raise ConnectKeyError(msg)
|
|
54
|
+
if isinstance(location, Path):
|
|
55
|
+
location = location
|
|
56
|
+
else:
|
|
57
|
+
location = Path(location)
|
|
58
|
+
location = location.expanduser().resolve(strict=False)
|
|
59
|
+
location.parent.mkdir(
|
|
60
|
+
parents=True,
|
|
61
|
+
exist_ok=True,
|
|
62
|
+
)
|
|
63
|
+
return location
|
|
64
|
+
|
|
65
|
+
def __str__(self) -> str:
|
|
66
|
+
return f"{self.__class__.__name__}"
|
|
67
|
+
|
|
68
|
+
def __repr__(self) -> str:
|
|
69
|
+
return f"{self.__class__.__name__}()"
|
|
70
|
+
|
|
71
|
+
def put_bytes(
|
|
72
|
+
self,
|
|
73
|
+
data: bytes,
|
|
74
|
+
location: Union[str, Path],
|
|
75
|
+
**kwargs: Any,
|
|
76
|
+
) -> None:
|
|
77
|
+
path = self._resolve_path(location)
|
|
78
|
+
try:
|
|
79
|
+
with path.open("wb") as f:
|
|
80
|
+
f.write(data)
|
|
81
|
+
except Exception as e:
|
|
82
|
+
raise ConnectFailedError(str(e)) from e
|
|
83
|
+
|
|
84
|
+
def get_bytes(
|
|
85
|
+
self,
|
|
86
|
+
location: Union[str, Path],
|
|
87
|
+
**kwargs: Any,
|
|
88
|
+
) -> bytes:
|
|
89
|
+
path = self._resolve_path(location)
|
|
90
|
+
try:
|
|
91
|
+
with path.open("rb") as f:
|
|
92
|
+
data = f.read()
|
|
93
|
+
return data
|
|
94
|
+
except Exception as e:
|
|
95
|
+
raise ConnectFailedError(str(e)) from e
|
|
96
|
+
|
|
97
|
+
def get_object(
|
|
98
|
+
self,
|
|
99
|
+
location: str,
|
|
100
|
+
**kwargs: Any,
|
|
101
|
+
) -> bytes:
|
|
102
|
+
return self.get_bytes(
|
|
103
|
+
location,
|
|
104
|
+
**kwargs,
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
def put_object(
|
|
108
|
+
self,
|
|
109
|
+
data: bytes,
|
|
110
|
+
location: str,
|
|
111
|
+
**kwargs: Any,
|
|
112
|
+
) -> ConnectRef:
|
|
113
|
+
self.put_bytes(
|
|
114
|
+
data,
|
|
115
|
+
location,
|
|
116
|
+
**kwargs,
|
|
117
|
+
)
|
|
118
|
+
return ConnectRef(
|
|
119
|
+
location=str(location),
|
|
120
|
+
kind=self.kind,
|
|
121
|
+
)
|