{"id":2053,"date":"2021-07-13T13:46:31","date_gmt":"2021-07-13T05:46:31","guid":{"rendered":"https:\/\/9incloud.com\/?p=2053"},"modified":"2023-11-12T17:16:25","modified_gmt":"2023-11-12T09:16:25","slug":"python-cli-publish-pypi","status":"publish","type":"post","link":"https:\/\/9incloud.com\/programming-language\/python\/python-cli-publish-pypi","title":{"rendered":"\u64b0\u5beb Python CLI \u5de5\u5177\u4e26\u767c\u4f48\u5230 PyPI"},"content":{"rendered":"\n

Python \u9664\u4e86\u5728 ML \u9818\u57df\u5f88\u7d05\u5916\uff0c\u5728 DevOps \u5de5\u5177\u4e0a\u4e5f\u662f\u6eff\u591a\u4eba\u7528\u7684\uff0c\u5982\u5f88\u6709\u540d\u7684 Ansible\uff0c\u5c31\u662f\u4ee5 Python \u8a9e\u8a00\u64b0\u5beb\uff0c\u518d\u8005\u5982 AWS CLI\uff0c\u4e5f\u662f Python \u64b0\u5beb\uff0c\u6240\u4ee5\u9019\u7bc7\u4f86\u4ecb\u7d39\u4e00\u4e0b CLI \u5957\u4ef6\u5de5\u5177\uff0c\u4e26\u4e14\u8aaa\u660e\u5982\u4f55\u4e0a\u50b3\u5230 pypi \u5f8c\uff0c\u7d93\u7531 pip install \u5f8c\uff0c\u5c31\u53ef\u4ee5\u6709\u73fe\u6210\u7684 CLI \u53ef\u4ee5\u4f7f\u7528\u4e86<\/p>\n\n\n\n

\u00a0<\/p>\n

Python CLI \u5de5\u5177<\/h3>\n

Python CLI \u5de5\u5177\u975e\u5e38\u7684\u591a\uff0c\u4f8b\u5982 \u9019\u7bc7<\/a> \u6709\u4ecb\u7d39\u4e0d\u5c11\u5de5\u5177\uff0c\u6211\u662f\u9078\u7528 PyInquirer<\/code>\uff0c\u6211\u60f3\u5404\u500b\u5957\u4ef6\u5404\u6709\u5229\u5f0a\uff0c\u6703\u9078\u7528\u9019\u5957\u7684\u539f\u56e0\u662f\u56e0\u70ba\u8a9e\u6cd5\u5f88\u660e\u66b8\u6613\u61c2\uff0c\u99ac\u4e0a\u4f86\u770b\u500b\u4f8b\u5b50<\/p>\n<\/div>\n\n\n\n

Python<\/span><\/path><\/path><\/svg><\/span>
questions = [<\/span><\/span>\n        {<\/span><\/span>\n            <\/span>'type'<\/span>: <\/span>'list'<\/span>,<\/span><\/span>\n            <\/span>'name'<\/span>: <\/span>'profile'<\/span>,<\/span><\/span>\n            <\/span>'message'<\/span>: <\/span>'Choose one AWS Profile to set default'<\/span>,<\/span><\/span>\n            <\/span># 'choices': profiles.keys(),<\/span><\/span>\n            <\/span>'choices'<\/span>: <\/span>list<\/span>(<\/span>filter<\/span>(<\/span>lambda<\/span> <\/span>profile<\/span>: <\/span>True<\/span> <\/span>if<\/span> profile.find(assume_role_profile_name) == -<\/span>1<\/span> <\/span>else<\/span> <\/span>False<\/span>, <\/span>list<\/span>(profiles.keys())))<\/span><\/span>\n        },<\/span><\/span>\n        {<\/span><\/span>\n            <\/span>'type'<\/span>: <\/span>'confirm'<\/span>,<\/span><\/span>\n            <\/span>'name'<\/span>: <\/span>'assume_role'<\/span>,<\/span><\/span>\n            <\/span>'message'<\/span>: <\/span>'Do you want to assume role for this profile?(Default false)'<\/span>,<\/span><\/span>\n            <\/span>'default'<\/span>: <\/span>False<\/span>,<\/span><\/span>\n        },<\/span><\/span>\n        {<\/span><\/span>\n            <\/span>'type'<\/span>: <\/span>'input'<\/span>,<\/span><\/span>\n            <\/span>'name'<\/span>: <\/span>'role_name'<\/span>,<\/span><\/span>\n            <\/span>'message'<\/span>: <\/span>'Input your role name to assume role?'<\/span>,<\/span><\/span>\n            <\/span>'when'<\/span>: <\/span>lambda<\/span> <\/span>answers<\/span>: answers[<\/span>'assume_role'<\/span>]<\/span><\/span>\n        },<\/span><\/span>\n        {<\/span><\/span>\n            <\/span>'type'<\/span>: <\/span>'input'<\/span>,<\/span><\/span>\n            <\/span>'name'<\/span>: <\/span>'duration_seconds'<\/span>,<\/span><\/span>\n            <\/span>'message'<\/span>: <\/span>'Input your maximum duration seconds of assume role?(Default 28800)'<\/span>,<\/span><\/span>\n            <\/span>'when'<\/span>: <\/span>lambda<\/span> <\/span>answers<\/span>: answers[<\/span>'assume_role'<\/span>]<\/span><\/span>\n        },<\/span><\/span>\n    ]<\/span><\/span>\n<\/span>\nanswers = prompt(questions, <\/span>style<\/span>=custom_style_2)<\/span><\/span>\n<\/span>\nis_assume_role = answers.get(<\/span>'assume_role'<\/span>)<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n

\u53ea\u8981\u5b9a\u7fa9\u597d dict \uff0c\u5e36\u5165 prompt \u4e2d\uff0c\u8f15\u9b06\u641e\u5b9a\uff0c\u800c\u53d6\u503c\u5c31\u7528 get \u65b9\u6cd5\u5373\u53ef\uff0c\u5f88\u7c21\u55ae\uff0c\u82e5 questions \u4e2d\u7684\u76ee\u984c\u6709\u76f8\u4f9d\u7684\u9700\u6c42\uff0c\u4f8b\u5982\u9019\u500b\u554f\u984c user \u56de\u7b54 yes\uff0c\u624d\u6703\u6709\u4e0b\u4e00\u500b\u554f\u984c\u7e7c\u7e8c\uff0c\u5728 PyInquirer<\/code> \u4e5f\u5f88\u5bb9\u6613\uff0c\u5982\u4f8b\u5b50\u4e2d\uff0c\u53ea\u6709\u6307\u5b9a when<\/code> \u7684\u8a2d\u5b9a\u5373\u53ef<\/p>\n

\u60f3\u770b\u9019\u500b\u6548\u679c\uff0c\u5230\u6211 GitHub repo Switch AWS Profile on local<\/a>\uff0cREADME \u5c31\u6709 gif \u52d5\u756b demo \u770b\u6548\u679c\uff0c\u771f\u7684\u662f\u8f15\u9b06\u6109\u5feb\uff0c\u7576\u7136\u4e5f\u9084\u6709\u66f4\u591a\u9032\u968e\u7684\u7528\u6cd5\u548c\u9078\u9805\uff0c\u53ef\u5230 PyInquirer<\/a> GitHub repo \u4e2d\u770b\u4e00\u4e0b\u5c31\u77e5\u9053<\/p>\n<\/div>\n\n\n\n

\u00a0<\/p>\n

\u6253\u5305\u4e0a\u50b3\u81f3 PyPI<\/h3>\n

PyPI \u5c31\u50cf PHP \u7684 composer \u6216 node \u7684 npm \u4e00\u6a23\uff0c\u662f\u767c\u4f48\u5957\u4ef6\u5e73\u53f0\uff0c\u8b93\u5168\u4e16\u754c\u53ef\u4ee5\u5171\u7528\uff0c\u800c\u5beb CLI \u7684\u76ee\u7684\uff0c\u7576\u7136\u5e0c\u671b\u53ef\u4ee5\u5b89\u88dd\u5f8c\u76f4\u63a5\u4e0b\u6307\u4ee4\u5c31\u80fd\u4f7f\u7528\uff0c\u4ee5\u6211\u81ea\u5df1\u7684 Switch AWS Profile on local<\/a> \u70ba\u4f8b\uff0c\u4f7f\u7528 pip \u5b89\u88dd\u5982<\/p>\n

pip3 install switch-aws-profile\n<\/code><\/pre>\n

\u99ac\u4e0a\u5c31\u6709 awssp<\/code> \u6307\u4ee4\u53ef\u4ee5\u4f7f\u7528\uff0c\u8981\u60f3\u9054\u5230\u9019\u500b\u6548\u679c\uff0c\u9996\u5148\u8981\u6e96\u5099\u4e00\u4e0b setup.py \u9019\u500b\u6a94\u6848\uff0c\u4f8b\u5982\u9019\u6a23\u8a2d\u5b9a<\/p>\n<\/div>\n\n\n\n

Python<\/span><\/path><\/path><\/svg><\/span>
from<\/span> setuptools <\/span>import<\/span> setup, find_packages<\/span><\/span>\n<\/span>\nwith<\/span> <\/span>open<\/span>(<\/span>"README.md"<\/span>, <\/span>"r"<\/span>, <\/span>encoding<\/span>=<\/span>"utf-8"<\/span>) <\/span>as<\/span> fh:<\/span><\/span>\n    long_description = fh.read()<\/span><\/span>\nsetup(<\/span><\/span>\n    <\/span>name<\/span>=<\/span>'switch-aws-profile'<\/span>,<\/span><\/span>\n    <\/span>version<\/span>=<\/span>'0.0.4'<\/span>,<\/span><\/span>\n    <\/span>author<\/span>=<\/span>'Chris Yang'<\/span>,<\/span><\/span>\n    <\/span>author_email<\/span>=<\/span>'kimisme9386@gmail.com'<\/span>,<\/span><\/span>\n    <\/span>license<\/span>=<\/span>'MIT'<\/span>,<\/span><\/span>\n    <\/span>description<\/span>=<\/span>'Switch AWS profile on local'<\/span>,<\/span><\/span>\n    <\/span>long_description<\/span>=long_description,<\/span><\/span>\n    <\/span>long_description_content_type<\/span>=<\/span>"text\/markdown"<\/span>,<\/span><\/span>\n    <\/span>url<\/span>=<\/span>'https:\/\/github.com\/kimisme9386\/cli-switch-aws-profile'<\/span>,<\/span><\/span>\n    <\/span>py_modules<\/span>=[<\/span>'switch_profile'<\/span>],<\/span><\/span>\n    <\/span>packages<\/span>=find_packages(),<\/span><\/span>\n    <\/span>install_requires<\/span>=[<\/span>'pyinquirer==1.0.3'<\/span>],<\/span><\/span>\n    <\/span>python_requires<\/span>=<\/span>'>=3.7'<\/span>,<\/span><\/span>\n    <\/span>classifiers<\/span>=[<\/span><\/span>\n        <\/span>"Programming Language :: Python :: 3.8"<\/span>,<\/span><\/span>\n        <\/span>"Operating System :: OS Independent"<\/span>,<\/span><\/span>\n    ],<\/span><\/span>\n    <\/span>entry_points<\/span>=<\/span>'''<\/span><\/span>\n        [console_scripts]<\/span><\/span>\n        awssp=switch_profile.app:main<\/span><\/span>\n    '''<\/span>,<\/span><\/span>\n    <\/span>package_dir<\/span>={<\/span>'switch_profile'<\/span>: <\/span>'switch_profile'<\/span>},<\/span><\/span>\n    <\/span>package_data<\/span>={<\/span>'switch_profile'<\/span>: [<\/span>'scripts\/*.sh'<\/span>]},<\/span><\/span>\n)<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n

\u5927\u90e8\u5206\u7684\u8a2d\u5b9a\u90fd\u4e0d\u96e3\u7406\u89e3\uff0c\u7279\u5225\u8981\u8b1b\u7684\u662f entry_points<\/code> \u9019\u500b\u8a2d\u5b9a\uff0c\u56e0\u70ba\u6211\u5011\u60f3\u8981 pip3 install \u5f8c\u5c31\u80fd\u6709\u5b89\u88dd\u7684\u6307\u4ee4\u53ef\u4ee5\u7528\uff0c\u6240\u4ee5\u4f7f\u7528 [console_scripts]<\/code>\uff0c\u800c\u4e4b\u5f8c\u4e0b\u4e00\u884c\u4e5f\u662f\u91cd\u9ede<\/p>\n

awssp=switch_profile.app:main\n<\/code><\/pre>\n

\u4ee5\u6b64\u7bc4\u4f8b\uff0cawssp \u5c31\u662f\u4f60\u60f3\u8981\u81ea\u8a02\u7684 command \u540d\u7a31\uff0c\u800c switch_profile.app:main<\/code> \u4e5f\u5c31\u662f\u5c0d\u61c9\u5230 Python \u7684 package \u548c modules \u7684\u6982\u5ff5\uff0c\u5982 switch_profile \u70ba\u8cc7\u6599\u593e\u4e0b\u6709 app.py \u9019\u500b\u6a94\u6848\uff0c\u800c main \u5c31\u662f\u88e1\u9762\u7684 function name<\/p>\n

\u9019\u908a\u4e5f\u984d\u5916\u63d0\u4e00\u4e0b\uff0c\u50cf\u9019\u500b\u7bc4\u4f8b\uff0c\u6211\u662f\u7528 python \u53bb call \u4e00\u500b Shell Script\uff0c\u6240\u4ee5\u9019\u500b shell script \u8981\u80fd\u88ab\u627e\u5230\u624d\u884c\uff0c\u6545\u8a2d\u5b9a\u4e0a\u7528 package_dir<\/code> \u548c package_data<\/code> \u4f86\u8655\u7406\u9019\u500b\u90e8\u5206\uff0c\u907f\u514d\u547c\u53eb\u4e0d\u5230 shell script<\/p>\n

\u8a2d\u5b9a\u597d setup.py \u5f8c\uff0c\u9700\u8981\u8072\u660e\u9019\u500b\u5957\u4ef6\u7684 LICENSE\uff0c\u53ef\u4ee5\u5230 choosealicense.com<\/a> \u9078\u4e00\u500b LICENSE \u8907\u88fd\u8cbc\u4e0a\u5373\u53ef\uff0c\u5230\u9019\u908a\u5f8c\uff0c\u5c31\u53ef\u4ee5\u5728\u672c\u5730\u6e2c\u8a66\u770b\u770b<\/p>\n

pipenv run python3 setup.py develop\n<\/code><\/pre>\n

\u770b\u662f\u7528\u54ea\u7a2e Python virtual environment \u5c31\u600e\u9ebc\u7528\uff0c\u9019\u908a\u4f7f\u7528 pipenv \u4f86\u7576\u7bc4\u4f8b\uff0c\u82e5\u6c92\u932f\u8aa4\uff0c\u5728\u865b\u64ec\u74b0\u5883\u4e2d\u7684 bin \u8cc7\u6599\u593e\u5c31\u6703\u51fa\u73fe awssp<\/code> \u9019\u500b\u6307\u4ee4\u53ef\u4ee5\u4f7f\u7528<\/p>\n

\u82e5\u6e2c\u8a66\u7121\u8aa4\uff0c\u518d\u4f86\u5c31\u662f\u6253\u5305\u548c\u4e0a\u50b3\u4e86\uff0c\u9996\u5148\u82e5\u6c92\u6709 PyPI \u5e33\u865f\uff0c\u9700\u8981\u53bb\u7533\u8acb\u4e00\u500b\uff0c\u4e4b\u5f8c\u5c31\u662f\u5148\u4f86\u6253\u5305<\/p>\n

pipenv run python3 setup.py bdist_wheel\n<\/code><\/pre>\n

\u6ce8\u610f\u9019\u908a\u4e5f\u53ef\u4ee5\u7528 sdist<\/code> \u4f86\u5b8c\u6574\u4f7f\u7528\u539f\u751f Python \u6253\u5305\u5c31\u597d\uff0c\u9019\u908a\u6211\u662f\u9078\u7528 wheel \u5de5\u5177\u4f86\u6253\u5305\uff0c\u6240\u4ee5\u7528 bdist_wheel<\/code>\uff0c\u82e5\u57f7\u884c\u6c92\u554f\u984c\uff0c\u6703\u7522\u751f build \u548c dist \u5169\u500b\u8cc7\u6599\u593e\uff0c\u518d\u4f86\u5c31\u662f\u4e0a\u50b3\u4e86<\/p>\n

pipenv run twine upload dist\/* -u __token__ -p ${PYPI_PASSWORD}\n<\/code><\/pre>\n

\u5176\u4e2d ${PYPI_PASSWORD} \u5c31\u662f\u53bb PyPI \u5f8c\u53f0\u65b0\u589e API tokens<\/code> \u5f8c\u5c31\u6703\u6709\u7684\uff0c\u9019\u5169\u500b\u6307\u4ee4\u6211\u662f\u5beb\u5728 GitHub Actions \u4e0a\uff0c\u53ef\u4ee5\u53c3\u8003\u9019\u88e1<\/a><\/p>\n

\u82e5\u57f7\u884c\u7121\u8aa4\uff0c\u5c31\u53ef\u4ee5\u5230 PyPI \u53bb\u770b\u770b\u767c\u4f48\u7684\u72c0\u6cc1\uff0c\u4e4b\u5f8c\u4fee\u6539\u7a0b\u5f0f\u78bc\u60f3\u8981\u518d\u767c\u4f48\u65b0\u7248\u672c\uff0c\u53ea\u8981\u4fee\u6539 setup.py \u4e2d\u7684 version\uff0c\u518d\u57f7\u884c\u9019\u5169\u500b\u6307\u4ee4\uff0c\u5c31\u80fd\u518d\u5ea6\u767c\u7248<\/p>\n<\/div>\n\n\n\n

\u00a0<\/p>\n

\u7d50\u8ad6<\/h3>\n

Python \u8a9e\u8a00\u96d6\u7136\u81ea\u5df1\u63a5\u89f8\u6c92\u50cf PHP \u90a3\u9ebc\u4e45\uff0c\u4f46\u8ddf PHP \u985e\u4f3c\u7684\u662f\uff0c\u8981\u5165\u9580\u90fd\u662f\u5bb9\u6613\u7684\uff0c\u4f46\u8981\u5beb\u7684\u597d\uff0c\u90a3\u5c31\u662f\u53e6\u5916\u4e00\u56de\u4e8b\u4e86\uff0c\u8981\u5beb\u7684\u597d\u53ef\u4ee5\u770b\u4e00\u4e0b Real Python \u7684 Best practice<\/a>\uff0c\u5b78\u7fd2\u4efb\u4f55\u8a9e\u8a00\u90fd\u9700\u8981\u82b1\u8cbb\u4e0d\u5c11\u529f\u592b\uff0c\u624d\u80fd\u771f\u7684\u5beb\u7684\u597d\uff0c\u5e0c\u671b\u5c0d Python \u6709\u8208\u8da3\u7684\u670b\u53cb\u90fd\u80fd\u958b\u5fc3\u5165\u9580\uff0c\u518d\u6301\u7e8c\u7cbe\u9032<\/p>\n<\/div>\n\n\n\n

\u00a0<\/p>\n

\u53c3\u8003\u8cc7\u6599<\/h3>\n