pax_global_header00006660000000000000000000000064140057444660014524gustar00rootroot0000000000000052 comment=ab80cc785e575bb377092380f2efc0c92a5d9dac shellingham-1.4.0/000077500000000000000000000000001400574446600140215ustar00rootroot00000000000000shellingham-1.4.0/.github/000077500000000000000000000000001400574446600153615ustar00rootroot00000000000000shellingham-1.4.0/.github/workflows/000077500000000000000000000000001400574446600174165ustar00rootroot00000000000000shellingham-1.4.0/.github/workflows/ci.yml000066400000000000000000000006161400574446600205370ustar00rootroot00000000000000on: [push, pull_request] name: ci jobs: linux: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - run: cd src; python -c 'import shellingham; print(shellingham.detect_shell())' macos: runs-on: macos-latest continue-on-error: true steps: - uses: actions/checkout@v2 - run: cd src; python -c 'import shellingham; print(shellingham.detect_shell())' shellingham-1.4.0/.gitignore000066400000000000000000000001071400574446600160070ustar00rootroot00000000000000.env .pytest_cache .venv __pycache__ build dist *.egg-info *.py[co] shellingham-1.4.0/CHANGELOG.rst000066400000000000000000000072531400574446600160510ustar00rootroot000000000000001.4.0 (2021-02-01) ================== Features -------- - On Windows, the full path to the shell executable is now returned instead of just the base name. `#42 `_ 1.3.2 (2020-02-12) ================== Bug Fixes --------- - Parse argument list to detect shells run via an interpreter, e.g. xonsh via Python. `#27 `_ 1.3.1 (2019-04-10) ================== Bug Fixes --------- - Fix a typo that prevents ash and csh from being detected. `#24 `_ 1.3.0 (2019-03-06) ================== Features -------- - Add `Almquist shell `_ (``ash``) detection support. `#20 `_ 1.2.8 (2018-12-16) ================== Bug Fixes --------- - Parse ``ps`` output according to how it is actually formatted, instead of incorrectly using ``shlex.split()``. `#14 `_ - Improve process parsing on Windows to so executables with non-ASCII names are handled better. `#16 `_ 1.2.7 (2018-10-15) ================== Bug Fixes --------- - Suppress subprocess errors from ``ps`` if the output is empty. `#15 `_ 1.2.6 (2018-09-14) ================== No significant changes. 1.2.5 (2018-09-14) ================== Bug Fixes --------- - Improve ``/proc`` content parsing robustness to not fail with non-decodable command line arguments. `#10 `_ 1.2.4 (2018-07-27) ================== Bug Fixes --------- - Fix exception on Windows when the executable path is too long to fit into the PROCESSENTRY32 struct. Generally the shell shouldn't be buried this deep, and we can always fix it when that actually happens, if ever. `#8 `_ 1.2.3 (2018-07-10) ======================= Bug Fixes --------- - Check a process’s argument list is valid before peeking into it. This works around a Heisenbug in VS Code, where a process read from ``/proc`` may contain an empty argument list. 1.2.2 (2018-07-09) ================== Features -------- - Support BSD-style ``/proc`` format. `#4 `_ Bug Fixes --------- - Better ``ps`` output decoding to fix compatibility. `#7 `_ 1.2.1 (2018-07-04) ================== Bug Fixes --------- - Fix login shell detection if it is ``chsh``-ed to point to an absolute path. `#6 `_ 1.2.0 (2018-07-04) ================== Features -------- - Prefer the ``/proc``-based approach on POSIX whenever it is likely to work. `#5 `_ 1.1.0 (2018-06-19) ================== Features -------- - Use ``/proc`` on Linux to build process tree. This is more reliable than ``ps``, which may not be available on a bare installation. `#3 `_ 1.0.1 (2018-06-19) ================== Bug Fixes --------- - Fix POSIX usage on Python 2 by providing more compatible arguments to parse ``ps`` results. Thanks to @glehmann for the patch. `#2 `_ 1.0.0.dev1 (2018-06-15) ======================= Bug Fixes --------- - Prevent the lookup from exploding when running in non-hierarchical process structure. (1-b2e9bef5) 1.0.0.dev0 (2018-06-14) ======================= Initial release. shellingham-1.4.0/LICENSE000066400000000000000000000013571400574446600150340ustar00rootroot00000000000000Copyright (c) 2018, Tzu-ping Chung Permission to use, copy, modify, and distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. shellingham-1.4.0/MANIFEST.in000066400000000000000000000000601400574446600155530ustar00rootroot00000000000000include LICENSE* README* include pyproject.toml shellingham-1.4.0/Pipfile000066400000000000000000000003001400574446600153250ustar00rootroot00000000000000[packages] shellingham = { path = '.', editable = true } [dev-packages] invoke = '*' parver = '*' pytest = '*' pytest-mock = '*' setl = '*' towncrier = '*' [scripts] release = 'inv release' shellingham-1.4.0/Pipfile.lock000066400000000000000000000513451400574446600162730ustar00rootroot00000000000000{ "_meta": { "hash": { "sha256": "47bb9b2e4bc994a062b02cec97541a7cadf81123a0a6dc8c8eb9083c81358cbc" }, "pipfile-spec": 6, "requires": {}, "sources": [ { "name": "pypi", "url": "https://pypi.org/simple", "verify_ssl": true } ] }, "default": { "shellingham": { "editable": true, "path": "." } }, "develop": { "arpeggio": { "hashes": [ "sha256:948ce06163a48a72c97f4fe79ad3d1c1330b6fec4f22ece182fb60ef60bd022b", "sha256:b9178917594bb9758002faed31e1e1c968b5ea7f2a8f78fd4a5b8fecaccfcfcd" ], "version": "==1.9.2" }, "attrs": { "hashes": [ "sha256:26b54ddbbb9ee1d34d5d3668dd37d6cf74990ab23c828c2888dccdceee395594", "sha256:fce7fc47dfc976152e82d53ff92fa0407700c21acd20886a13777a0d20e655dc" ], "version": "==20.2.0" }, "bleach": { "hashes": [ "sha256:52b5919b81842b1854196eaae5ca29679a2f2e378905c346d3ca8227c2c66080", "sha256:9f8ccbeb6183c6e6cddea37592dfb0167485c1e3b13b3363bc325aa8bda3adbd" ], "version": "==3.2.1" }, "cached-property": { "hashes": [ "sha256:9fa5755838eecbb2d234c3aa390bd80fbd3ac6b6869109bfc1b499f7bd89a130", "sha256:df4f613cf7ad9a588cc381aaf4a512d26265ecebd5eb9e1ba12f1319eb85a6a0" ], "version": "==1.5.2" }, "certifi": { "hashes": [ "sha256:5930595817496dd21bb8dc35dad090f1c2cd0adfaf21204bf6732ca5d8ee34d3", "sha256:8fc0819f1f30ba15bdb34cceffb9ef04d99f420f68eb75d901e9560b8749fc41" ], "version": "==2020.6.20" }, "cffi": { "hashes": [ "sha256:005f2bfe11b6745d726dbb07ace4d53f057de66e336ff92d61b8c7e9c8f4777d", "sha256:09e96138280241bd355cd585148dec04dbbedb4f46128f340d696eaafc82dd7b", "sha256:0b1ad452cc824665ddc682400b62c9e4f5b64736a2ba99110712fdee5f2505c4", "sha256:0ef488305fdce2580c8b2708f22d7785ae222d9825d3094ab073e22e93dfe51f", "sha256:15f351bed09897fbda218e4db5a3d5c06328862f6198d4fb385f3e14e19decb3", "sha256:22399ff4870fb4c7ef19fff6eeb20a8bbf15571913c181c78cb361024d574579", "sha256:23e5d2040367322824605bc29ae8ee9175200b92cb5483ac7d466927a9b3d537", "sha256:2791f68edc5749024b4722500e86303a10d342527e1e3bcac47f35fbd25b764e", "sha256:2f9674623ca39c9ebe38afa3da402e9326c245f0f5ceff0623dccdac15023e05", "sha256:3363e77a6176afb8823b6e06db78c46dbc4c7813b00a41300a4873b6ba63b171", "sha256:33c6cdc071ba5cd6d96769c8969a0531be2d08c2628a0143a10a7dcffa9719ca", "sha256:3b8eaf915ddc0709779889c472e553f0d3e8b7bdf62dab764c8921b09bf94522", "sha256:3cb3e1b9ec43256c4e0f8d2837267a70b0e1ca8c4f456685508ae6106b1f504c", "sha256:3eeeb0405fd145e714f7633a5173318bd88d8bbfc3dd0a5751f8c4f70ae629bc", "sha256:44f60519595eaca110f248e5017363d751b12782a6f2bd6a7041cba275215f5d", "sha256:4d7c26bfc1ea9f92084a1d75e11999e97b62d63128bcc90c3624d07813c52808", "sha256:529c4ed2e10437c205f38f3691a68be66c39197d01062618c55f74294a4a4828", "sha256:6642f15ad963b5092d65aed022d033c77763515fdc07095208f15d3563003869", "sha256:85ba797e1de5b48aa5a8427b6ba62cf69607c18c5d4eb747604b7302f1ec382d", "sha256:8f0f1e499e4000c4c347a124fa6a27d37608ced4fe9f7d45070563b7c4c370c9", "sha256:a624fae282e81ad2e4871bdb767e2c914d0539708c0f078b5b355258293c98b0", "sha256:b0358e6fefc74a16f745afa366acc89f979040e0cbc4eec55ab26ad1f6a9bfbc", "sha256:bbd2f4dfee1079f76943767fce837ade3087b578aeb9f69aec7857d5bf25db15", "sha256:bf39a9e19ce7298f1bd6a9758fa99707e9e5b1ebe5e90f2c3913a47bc548747c", "sha256:c11579638288e53fc94ad60022ff1b67865363e730ee41ad5e6f0a17188b327a", "sha256:c150eaa3dadbb2b5339675b88d4573c1be3cb6f2c33a6c83387e10cc0bf05bd3", "sha256:c53af463f4a40de78c58b8b2710ade243c81cbca641e34debf3396a9640d6ec1", "sha256:cb763ceceae04803adcc4e2d80d611ef201c73da32d8f2722e9d0ab0c7f10768", "sha256:cc75f58cdaf043fe6a7a6c04b3b5a0e694c6a9e24050967747251fb80d7bce0d", "sha256:d80998ed59176e8cba74028762fbd9b9153b9afc71ea118e63bbf5d4d0f9552b", "sha256:de31b5164d44ef4943db155b3e8e17929707cac1e5bd2f363e67a56e3af4af6e", "sha256:e66399cf0fc07de4dce4f588fc25bfe84a6d1285cc544e67987d22663393926d", "sha256:f0620511387790860b249b9241c2f13c3a80e21a73e0b861a2df24e9d6f56730", "sha256:f4eae045e6ab2bb54ca279733fe4eb85f1effda392666308250714e01907f394", "sha256:f92cdecb618e5fa4658aeb97d5eb3d2f47aa94ac6477c6daf0f306c5a3b9e6b1", "sha256:f92f789e4f9241cd262ad7a555ca2c648a98178a953af117ef7fad46aa1d5591" ], "version": "==1.14.3" }, "chardet": { "hashes": [ "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691" ], "version": "==3.0.4" }, "click": { "hashes": [ "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a", "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc" ], "version": "==7.1.2" }, "colorama": { "hashes": [ "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b", "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2" ], "version": "==0.4.4" }, "cryptography": { "hashes": [ "sha256:22f8251f68953553af4f9c11ec5f191198bc96cff9f0ac5dd5ff94daede0ee6d", "sha256:284e275e3c099a80831f9898fb5c9559120d27675c3521278faba54e584a7832", "sha256:3e17d02941c0f169c5b877597ca8be895fca0e5e3eb882526a74aa4804380a98", "sha256:52a47e60953679eea0b4d490ca3c241fb1b166a7b161847ef4667dfd49e7699d", "sha256:57b8c1ed13b8aa386cabbfde3be175d7b155682470b0e259fecfe53850967f8a", "sha256:6a8f64ed096d13f92d1f601a92d9fd1f1025dc73a2ca1ced46dcf5e0d4930943", "sha256:6e8a3c7c45101a7eeee93102500e1b08f2307c717ff553fcb3c1127efc9b6917", "sha256:7ef41304bf978f33cfb6f43ca13bb0faac0c99cda33693aa20ad4f5e34e8cb8f", "sha256:87c2fffd61e934bc0e2c927c3764c20b22d7f5f7f812ee1a477de4c89b044ca6", "sha256:88069392cd9a1e68d2cfd5c3a2b0d72a44ef3b24b8977a4f7956e9e3c4c9477a", "sha256:8a0866891326d3badb17c5fd3e02c926b635e8923fa271b4813cd4d972a57ff3", "sha256:8f0fd8b0751d75c4483c534b209e39e918f0d14232c0d8a2a76e687f64ced831", "sha256:9a07e6d255053674506091d63ab4270a119e9fc83462c7ab1dbcb495b76307af", "sha256:9a8580c9afcdcddabbd064c0a74f337af74ff4529cdf3a12fa2e9782d677a2e5", "sha256:bd80bc156d3729b38cb227a5a76532aef693b7ac9e395eea8063ee50ceed46a5", "sha256:d1cbc3426e6150583b22b517ef3720036d7e3152d428c864ff0f3fcad2b97591", "sha256:e15ac84dcdb89f92424cbaca4b0b34e211e7ce3ee7b0ec0e4f3c55cee65fae5a", "sha256:e4789b84f8dedf190148441f7c5bfe7244782d9cbb194a36e17b91e7d3e1cca9", "sha256:f01c9116bfb3ad2831e125a73dcd957d173d6ddca7701528eff1e7d97972872c", "sha256:f0e3986f6cce007216b23c490f093f35ce2068f3c244051e559f647f6731b7ae", "sha256:f2aa3f8ba9e2e3fd49bd3de743b976ab192fbf0eb0348cebde5d2a9de0090a9f", "sha256:fb70a4cedd69dc52396ee114416a3656e011fb0311fca55eb55c7be6ed9c8aef" ], "index": "pypi", "version": "==3.2" }, "docutils": { "hashes": [ "sha256:0c5b78adfbf7762415433f5515cd5c9e762339e23369dbe8000d84a4bf4ab3af", "sha256:c2de3a60e9e7d07be26b7f2b00ca0309c207e06c100f9cc2a94931fc75a478fc" ], "version": "==0.16" }, "idna": { "hashes": [ "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6", "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0" ], "version": "==2.10" }, "incremental": { "hashes": [ "sha256:717e12246dddf231a349175f48d74d93e2897244939173b01974ab6661406b9f", "sha256:7b751696aaf36eebfab537e458929e194460051ccad279c72b755a167eebd4b3" ], "version": "==17.5.0" }, "invoke": { "hashes": [ "sha256:87b3ef9d72a1667e104f89b159eaf8a514dbf2f3576885b2bbdefe74c3fb2132", "sha256:93e12876d88130c8e0d7fd6618dd5387d6b36da55ad541481dfa5e001656f134", "sha256:de3f23bfe669e3db1085789fd859eb8ca8e0c5d9c20811e2407fa042e8a5e15d" ], "index": "pypi", "version": "==1.4.1" }, "jeepney": { "hashes": [ "sha256:3479b861cc2b6407de5188695fa1a8d57e5072d7059322469b62628869b8e36e", "sha256:d6c6b49683446d2407d2fe3acb7a368a77ff063f9182fe427da15d622adc24cf" ], "markers": "sys_platform == 'linux'", "version": "==0.4.3" }, "jinja2": { "hashes": [ "sha256:89aab215427ef59c34ad58735269eb58b1a5808103067f7bb9d5836c651b3bb0", "sha256:f0a4641d3cf955324a89c04f3d94663aa4d638abe8f733ecd3582848e1c37035" ], "version": "==2.11.2" }, "keyring": { "hashes": [ "sha256:4e34ea2fdec90c1c43d6610b5a5fafa1b9097db1802948e90caf5763974b8f8d", "sha256:9aeadd006a852b78f4b4ef7c7556c2774d2432bbef8ee538a3e9089ac8b11466" ], "version": "==21.4.0" }, "markupsafe": { "hashes": [ "sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473", "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161", "sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235", "sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5", "sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42", "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff", "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b", "sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1", "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e", "sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183", "sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66", "sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b", "sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1", "sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15", "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1", "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e", "sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b", "sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905", "sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735", "sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d", "sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e", "sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d", "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c", "sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21", "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2", "sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5", "sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b", "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6", "sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f", "sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f", "sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2", "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7", "sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be" ], "version": "==1.1.1" }, "more-itertools": { "hashes": [ "sha256:6f83822ae94818eae2612063a5101a7311e68ae8002005b5e05f03fd74a86a20", "sha256:9b30f12df9393f0d28af9210ff8efe48d10c94f73e5daf886f10c4b0b0b4f03c" ], "version": "==8.5.0" }, "packaging": { "hashes": [ "sha256:4357f74f47b9c12db93624a82154e9b120fa8293699949152b22065d556079f8", "sha256:998416ba6962ae7fbd6596850b80e17859a5753ba17c32284f67bfff33784181" ], "version": "==20.4" }, "parver": { "hashes": [ "sha256:1b37a691af145a3a193eff269d53ba5b2ab16dfbb65d47d85360755919f5fe4b", "sha256:72d056b8f8883ac90eef5554a9c8a47fac39d3b66479f3d2c8d5bc21b849cdba" ], "index": "pypi", "version": "==0.2.1" }, "pep517": { "hashes": [ "sha256:3985b91ebf576883efe5fa501f42a16de2607684f3797ddba7202b71b7d0da51", "sha256:aeb78601f2d1aa461960b43add204cc7955667687fbcf9cdb5170f00556f117f" ], "version": "==0.9.1" }, "pkginfo": { "hashes": [ "sha256:a6a4ac943b496745cec21f14f021bbd869d5e9b4f6ec06918cffea5a2f4b9193", "sha256:ce14d7296c673dc4c61c759a0b6c14bae34e34eb819c0017bb6ca5b7292c56e9" ], "version": "==1.6.1" }, "pluggy": { "hashes": [ "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0", "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d" ], "version": "==0.13.1" }, "py": { "hashes": [ "sha256:366389d1db726cd2fcfc79732e75410e5fe4d31db13692115529d34069a043c2", "sha256:9ca6883ce56b4e8da7e79ac18787889fa5206c79dcc67fb065376cd2fe03f342" ], "version": "==1.9.0" }, "pycparser": { "hashes": [ "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0", "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705" ], "version": "==2.20" }, "pygments": { "hashes": [ "sha256:381985fcc551eb9d37c52088a32914e00517e57f4a21609f48141ba08e193fa0", "sha256:88a0bbcd659fcb9573703957c6b9cff9fab7295e6e76db54c9d00ae42df32773" ], "version": "==2.7.2" }, "pyparsing": { "hashes": [ "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1", "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b" ], "version": "==2.4.7" }, "pytest": { "hashes": [ "sha256:0d5fe9189a148acc3c3eb2ac8e1ac0742cb7618c084f3d228baaec0c254b318d", "sha256:ff615c761e25eb25df19edddc0b970302d2a9091fbce0e7213298d85fb61fef6" ], "index": "pypi", "version": "==5.3.5" }, "pytest-mock": { "hashes": [ "sha256:b35eb281e93aafed138db25c8772b95d3756108b601947f89af503f8c629413f", "sha256:cb67402d87d5f53c579263d37971a164743dc33c159dfb4fb4a86f37c5552307" ], "index": "pypi", "version": "==2.0.0" }, "readme-renderer": { "hashes": [ "sha256:267854ac3b1530633c2394ead828afcd060fc273217c42ac36b6be9c42cd9a9d", "sha256:6b7e5aa59210a40de72eb79931491eaf46fefca2952b9181268bd7c7c65c260a" ], "version": "==28.0" }, "requests": { "hashes": [ "sha256:b3559a131db72c33ee969480840fff4bb6dd111de7dd27c8ee1f820f4f00231b", "sha256:fe75cc94a9443b9246fc7049224f75604b113c36acb93f87b80ed42c44cbb898" ], "version": "==2.24.0" }, "requests-toolbelt": { "hashes": [ "sha256:380606e1d10dc85c3bd47bf5a6095f815ec007be7a8b69c878507068df059e6f", "sha256:968089d4584ad4ad7c171454f0a5c6dac23971e9472521ea3b6d49d610aa6fc0" ], "version": "==0.9.1" }, "rfc3986": { "hashes": [ "sha256:112398da31a3344dc25dbf477d8df6cb34f9278a94fee2625d89e4514be8bb9d", "sha256:af9147e9aceda37c91a05f4deb128d4b4b49d6b199775fd2d2927768abdc8f50" ], "version": "==1.4.0" }, "secretstorage": { "hashes": [ "sha256:15da8a989b65498e29be338b3b279965f1b8f09b9668bd8010da183024c8bff6", "sha256:b5ec909dde94d4ae2fa26af7c089036997030f0cf0a5cb372b4cccabd81c143b" ], "markers": "sys_platform == 'linux'", "version": "==3.1.2" }, "setl": { "hashes": [ "sha256:45c7ef74b5df6e143801cbe716344b504cde3da6a0f51cfe30e799e9d16047c4", "sha256:a305a77ecedb5affc1f49bbb4f0a3d3a382399f2227444920caf809af89848d7" ], "index": "pypi", "version": "==0.8.5" }, "six": { "hashes": [ "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259", "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced" ], "version": "==1.15.0" }, "toml": { "hashes": [ "sha256:926b612be1e5ce0634a2ca03470f95169cf16f939018233a670519cb4ac58b0f", "sha256:bda89d5935c2eac546d648028b9901107a595863cb36bae0c73ac804a9b4ce88" ], "version": "==0.10.1" }, "towncrier": { "hashes": [ "sha256:48251a1ae66d2cf7e6fa5552016386831b3e12bb3b2d08eb70374508c17a8196", "sha256:de19da8b8cb44f18ea7ed3a3823087d2af8fcf497151bb9fd1e1b092ff56ed8d" ], "index": "pypi", "version": "==19.2.0" }, "tqdm": { "hashes": [ "sha256:9ad44aaf0fc3697c06f6e05c7cf025dd66bc7bcb7613c66d85f4464c47ac8fad", "sha256:ef54779f1c09f346b2b5a8e5c61f96fbcb639929e640e59f8cf810794f406432" ], "version": "==4.51.0" }, "twine": { "hashes": [ "sha256:34352fd52ec3b9d29837e6072d5a2a7c6fe4290e97bba46bb8d478b5c598f7ab", "sha256:ba9ff477b8d6de0c89dd450e70b2185da190514e91c42cc62f96850025c10472" ], "version": "==3.2.0" }, "urllib3": { "hashes": [ "sha256:8d7eaa5a82a1cac232164990f04874c594c9453ec55eef02eab885aa02fc17a2", "sha256:f5321fbe4bf3fefa0efd0bfe7fb14e90909eb62a48ccda331726b4319897dd5e" ], "version": "==1.25.11" }, "wcwidth": { "hashes": [ "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784", "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83" ], "version": "==0.2.5" }, "webencodings": { "hashes": [ "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78", "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923" ], "version": "==0.5.1" } } } shellingham-1.4.0/README.rst000066400000000000000000000050041400574446600155070ustar00rootroot00000000000000============================================= Shellingham: Tool to Detect Surrounding Shell ============================================= .. image:: https://img.shields.io/pypi/v/shellingham.svg :target: https://pypi.org/project/shellingham/ Shellingham detects what shell the current Python executable is running in. Usage ===== :: >>> import shellingham >>> shellingham.detect_shell() ('bash', '/bin/bash') ``detect_shell`` pokes around the process's running environment to determine what shell it is run in. It returns a 2-tuple: * The shell name, always lowercased. * The command used to run the shell. ``ShellDetectionFailure`` is raised if ``detect_shell`` fails to detect the surrounding shell. Notes ===== * The shell name is always lowercased. * On Windows, the shell name is the name of the executable, minus the file extension. * Currently the command only contains the executable name on Windows, even if the command is invoked by the full path. This may change in the future. Notes for Application Developers ================================ Remember, your application's user is not necessarily using a shell. Shellingham raises ``ShellDetectionFailure`` if there is no shell to detect, but *your application should almost never do this to your user*. A practical approach to this is to wrap ``detect_shell`` in a try block, and provide a sane default on failure:: try: shell = shellingham.detect_shell() except shellingham.ShellDetectionFailure: shell = provide_default() There are a few choices for you to choose from. * The POSIX standard mandates the environment variable ``SHELL`` to refer to "the user's preferred command language interpreter". This is always available (even if the user is not in an interactive session), and likely the correct choice to launch an interactive sub-shell with. * A command ``sh`` is almost guarenteed to exist, likely at ``/bin/sh``, since several POSIX tools rely on it. This should be suitable if you want to run a (possibly non-interactive) script. * All versions of DOS and Windows have an environment variable ``COMSPEC``. This can always be used to launch a usable command prompt (e.g. `cmd.exe` on Windows). Here's a simple implementation to provide a default shell:: import os def provide_default(): if os.name == 'posix': return os.environ['SHELL'] elif os.name == 'nt': return os.environ['COMSPEC'] raise NotImplementedError(f'OS {os.name!r} support not available') shellingham-1.4.0/news/000077500000000000000000000000001400574446600147755ustar00rootroot00000000000000shellingham-1.4.0/news/.gitignore000066400000000000000000000000141400574446600167600ustar00rootroot00000000000000!.gitignore shellingham-1.4.0/pyproject.toml000066400000000000000000000013211400574446600167320ustar00rootroot00000000000000[build-system] requires = ["setuptools", "wheel"] [tool.black] line-length = 79 [tool.towncrier] package = "shellingham" package_dir = "src" filename = "CHANGELOG.rst" directory = "news/" title_format = "{version} ({project_date})" issue_format = "`#{issue} `_" template = "tasks/CHANGELOG.rst.jinja2" [[tool.towncrier.type]] directory = "feature" name = "Features" showcontent = true [[tool.towncrier.type]] directory = "bugfix" name = "Bug Fixes" showcontent = true [[tool.towncrier.type]] directory = "trivial" name = "Trivial Changes" showcontent = false [[tool.towncrier.type]] directory = "removal" name = "Removals and Deprecations" showcontent = true shellingham-1.4.0/setup.cfg000066400000000000000000000021141400574446600156400ustar00rootroot00000000000000[metadata] name = shellingham version = attr: shellingham.__version__ description = Tool to Detect Surrounding Shell url = https://github.com/sarugaku/shellingham author = Tzu-ping Chung author_email = uranusjr@gmail.com long_description = file: README.rst long_description_content_type = text/x-rst license = ISC License keywords = shell classifier = Development Status :: 3 - Alpha Environment :: Console Intended Audience :: Developers License :: OSI Approved :: ISC License (ISCL) Operating System :: OS Independent Programming Language :: Python :: 2 Programming Language :: Python :: 2.6 Programming Language :: Python :: 2.7 Programming Language :: Python :: 3 Programming Language :: Python :: 3.4 Programming Language :: Python :: 3.5 Programming Language :: Python :: 3.6 Topic :: Software Development :: Libraries :: Python Modules [options] package_dir = = src packages = find: python_requires = >=2.6,!=3.0,!=3.1,!=3.2,!=3.3 install_requires = zip_safe = true [options.packages.find] where = src [bdist_wheel] universal = 1 shellingham-1.4.0/setup.py000066400000000000000000000000451400574446600155320ustar00rootroot00000000000000from setuptools import setup setup() shellingham-1.4.0/src/000077500000000000000000000000001400574446600146105ustar00rootroot00000000000000shellingham-1.4.0/src/shellingham/000077500000000000000000000000001400574446600171035ustar00rootroot00000000000000shellingham-1.4.0/src/shellingham/__init__.py000066400000000000000000000011741400574446600212170ustar00rootroot00000000000000import importlib import os from ._core import ShellDetectionFailure __version__ = "1.4.0" def detect_shell(pid=None, max_depth=10): name = os.name try: impl = importlib.import_module(".{}".format(name), __name__) except ImportError: message = "Shell detection not implemented for {0!r}".format(name) raise RuntimeError(message) try: get_shell = impl.get_shell except AttributeError: raise RuntimeError("get_shell not implemented for {0!r}".format(name)) shell = get_shell(pid, max_depth=max_depth) if shell: return shell raise ShellDetectionFailure() shellingham-1.4.0/src/shellingham/_core.py000066400000000000000000000004461400574446600205500ustar00rootroot00000000000000SHELL_NAMES = ( {"sh", "bash", "dash", "ash"} # Bourne. | {"csh", "tcsh"} # C. | {"ksh", "zsh", "fish"} # Common alternatives. | {"cmd", "powershell", "pwsh"} # Microsoft. | {"elvish", "xonsh"} # More exotic. ) class ShellDetectionFailure(EnvironmentError): pass shellingham-1.4.0/src/shellingham/nt.py000066400000000000000000000106441400574446600201030ustar00rootroot00000000000000import contextlib import ctypes import os from ctypes.wintypes import ( BOOL, CHAR, DWORD, HANDLE, LONG, LPWSTR, MAX_PATH, PDWORD, ULONG, ) from shellingham._core import SHELL_NAMES INVALID_HANDLE_VALUE = HANDLE(-1).value ERROR_NO_MORE_FILES = 18 ERROR_INSUFFICIENT_BUFFER = 122 TH32CS_SNAPPROCESS = 2 PROCESS_QUERY_LIMITED_INFORMATION = 0x1000 kernel32 = ctypes.windll.kernel32 def _check_handle(error_val=0): def check(ret, func, args): if ret == error_val: raise ctypes.WinError() return ret return check def _check_expected(expected): def check(ret, func, args): if ret: return True code = ctypes.GetLastError() if code == expected: return False raise ctypes.WinError(code) return check class ProcessEntry32(ctypes.Structure): _fields_ = ( ("dwSize", DWORD), ("cntUsage", DWORD), ("th32ProcessID", DWORD), ("th32DefaultHeapID", ctypes.POINTER(ULONG)), ("th32ModuleID", DWORD), ("cntThreads", DWORD), ("th32ParentProcessID", DWORD), ("pcPriClassBase", LONG), ("dwFlags", DWORD), ("szExeFile", CHAR * MAX_PATH), ) kernel32.CloseHandle.argtypes = [HANDLE] kernel32.CloseHandle.restype = BOOL kernel32.CreateToolhelp32Snapshot.argtypes = [DWORD, DWORD] kernel32.CreateToolhelp32Snapshot.restype = HANDLE kernel32.CreateToolhelp32Snapshot.errcheck = _check_handle( # type: ignore INVALID_HANDLE_VALUE, ) kernel32.Process32First.argtypes = [HANDLE, ctypes.POINTER(ProcessEntry32)] kernel32.Process32First.restype = BOOL kernel32.Process32First.errcheck = _check_expected( # type: ignore ERROR_NO_MORE_FILES, ) kernel32.Process32Next.argtypes = [HANDLE, ctypes.POINTER(ProcessEntry32)] kernel32.Process32Next.restype = BOOL kernel32.Process32Next.errcheck = _check_expected( # type: ignore ERROR_NO_MORE_FILES, ) kernel32.GetCurrentProcessId.argtypes = [] kernel32.GetCurrentProcessId.restype = DWORD kernel32.OpenProcess.argtypes = [DWORD, BOOL, DWORD] kernel32.OpenProcess.restype = HANDLE kernel32.OpenProcess.errcheck = _check_handle( # type: ignore INVALID_HANDLE_VALUE, ) kernel32.QueryFullProcessImageNameW.argtypes = [HANDLE, DWORD, LPWSTR, PDWORD] kernel32.QueryFullProcessImageNameW.restype = BOOL kernel32.QueryFullProcessImageNameW.errcheck = _check_expected( # type: ignore ERROR_INSUFFICIENT_BUFFER, ) @contextlib.contextmanager def _handle(f, *args, **kwargs): handle = f(*args, **kwargs) try: yield handle finally: kernel32.CloseHandle(handle) def _iter_processes(): f = kernel32.CreateToolhelp32Snapshot with _handle(f, TH32CS_SNAPPROCESS, 0) as snap: entry = ProcessEntry32() entry.dwSize = ctypes.sizeof(entry) ret = kernel32.Process32First(snap, entry) while ret: yield entry ret = kernel32.Process32Next(snap, entry) def _get_full_path(proch): size = DWORD(MAX_PATH) while True: path_buff = ctypes.create_unicode_buffer("", size.value) if kernel32.QueryFullProcessImageNameW(proch, 0, path_buff, size): return path_buff.value size.value *= 2 def get_shell(pid=None, max_depth=10): proc_map = { proc.th32ProcessID: (proc.th32ParentProcessID, proc.szExeFile) for proc in _iter_processes() } pid = pid or os.getpid() for _ in range(0, max_depth + 1): try: ppid, executable = proc_map[pid] except KeyError: # No such process? Give up. break # The executable name would be encoded with the current code page if # we're in ANSI mode (usually). Try to decode it into str/unicode, # replacing invalid characters to be safe (not thoeratically necessary, # I think). Note that we need to use 'mbcs' instead of encoding # settings from sys because this is from the Windows API, not Python # internals (which those settings reflect). (pypa/pipenv#3382) if isinstance(executable, bytes): executable = executable.decode("mbcs", "replace") name = executable.rpartition(".")[0].lower() if name not in SHELL_NAMES: pid = ppid continue key = PROCESS_QUERY_LIMITED_INFORMATION with _handle(kernel32.OpenProcess, key, 0, pid) as proch: return (name, _get_full_path(proch)) return None shellingham-1.4.0/src/shellingham/posix/000077500000000000000000000000001400574446600202455ustar00rootroot00000000000000shellingham-1.4.0/src/shellingham/posix/__init__.py000066400000000000000000000053401400574446600223600ustar00rootroot00000000000000import os import re from .._core import SHELL_NAMES, ShellDetectionFailure from . import proc, ps def _get_process_mapping(): """Select a way to obtain process information from the system. * `/proc` is used if supported. * The system `ps` utility is used as a fallback option. """ for impl in (proc, ps): try: mapping = impl.get_process_mapping() except EnvironmentError: continue return mapping raise ShellDetectionFailure("compatible proc fs or ps utility is required") def _iter_process_args(mapping, pid, max_depth): """Traverse up the tree and yield each process's argument list.""" for _ in range(max_depth): try: proc = mapping[pid] except KeyError: # We've reached the root process. Give up. break if proc.args: # Persumably the process should always have a name? yield proc.args pid = proc.ppid # Go up one level. def _get_login_shell(proc_cmd): """Form shell information from SHELL environ if possible.""" login_shell = os.environ.get("SHELL", "") if login_shell: proc_cmd = login_shell else: proc_cmd = proc_cmd[1:] return (os.path.basename(proc_cmd).lower(), proc_cmd) _INTERPRETER_SHELL_NAMES = [ (re.compile(r"^python(\d+(\.\d+)?)?$"), {"xonsh"}), ] def _get_interpreter_shell(proc_name, proc_args): """Get shell invoked via an interpreter. Some shells are implemented on, and invoked with an interpreter, e.g. xonsh is commonly executed with an executable Python script. This detects what script the interpreter is actually running, and check whether that looks like a shell. See sarugaku/shellingham#26 for rational. """ for pattern, shell_names in _INTERPRETER_SHELL_NAMES: if not pattern.match(proc_name): continue for arg in proc_args: name = os.path.basename(arg).lower() if os.path.isfile(arg) and name in shell_names: return (name, arg) return None def _get_shell(cmd, *args): if cmd.startswith("-"): # Login shell! Let's use this. return _get_login_shell(cmd) name = os.path.basename(cmd).lower() if name in SHELL_NAMES: # Command looks like a shell. return (name, cmd) shell = _get_interpreter_shell(name, args) if shell: return shell return None def get_shell(pid=None, max_depth=10): """Get the shell that the supplied pid or os.getpid() is running in.""" pid = str(pid or os.getpid()) mapping = _get_process_mapping() for proc_args in _iter_process_args(mapping, pid, max_depth): shell = _get_shell(*proc_args) if shell: return shell return None shellingham-1.4.0/src/shellingham/posix/_core.py000066400000000000000000000001211400574446600217000ustar00rootroot00000000000000import collections Process = collections.namedtuple("Process", "args pid ppid") shellingham-1.4.0/src/shellingham/posix/proc.py000066400000000000000000000040751400574446600215700ustar00rootroot00000000000000import io import os import re import sys from ._core import Process STAT_PPID = 3 STAT_TTY = 6 STAT_PATTERN = re.compile(r"\(.+\)|\S+") def detect_proc(): """Detect /proc filesystem style. This checks the /proc/{pid} directory for possible formats. Returns one of the followings as str: * `stat`: Linux-style, i.e. ``/proc/{pid}/stat``. * `status`: BSD-style, i.e. ``/proc/{pid}/status``. """ pid = os.getpid() for name in ("stat", "status"): if os.path.exists(os.path.join("/proc", str(pid), name)): return name raise ProcFormatError("unsupported proc format") def _get_stat(pid, name): path = os.path.join("/proc", str(pid), name) with io.open(path, encoding="ascii", errors="replace") as f: # We only care about TTY and PPID -- all numbers. parts = STAT_PATTERN.findall(f.read()) return parts[STAT_TTY], parts[STAT_PPID] def _get_cmdline(pid): path = os.path.join("/proc", str(pid), "cmdline") encoding = sys.getfilesystemencoding() or "utf-8" with io.open(path, encoding=encoding, errors="replace") as f: # XXX: Command line arguments can be arbitrary byte sequences, not # necessarily decodable. For Shellingham's purpose, however, we don't # care. (pypa/pipenv#2820) # cmdline appends an extra NULL at the end, hence the [:-1]. return tuple(f.read().split("\0")[:-1]) class ProcFormatError(EnvironmentError): pass def get_process_mapping(): """Try to look up the process tree via the /proc interface.""" stat_name = detect_proc() self_tty = _get_stat(os.getpid(), stat_name)[0] processes = {} for pid in os.listdir("/proc"): if not pid.isdigit(): continue try: tty, ppid = _get_stat(pid, stat_name) if tty != self_tty: continue args = _get_cmdline(pid) processes[pid] = Process(args=args, pid=pid, ppid=ppid) except IOError: # Process has disappeared - just ignore it. continue return processes shellingham-1.4.0/src/shellingham/posix/ps.py000066400000000000000000000030451400574446600212430ustar00rootroot00000000000000import errno import subprocess import sys from ._core import Process class PsNotAvailable(EnvironmentError): pass def get_process_mapping(): """Try to look up the process tree via the output of `ps`.""" try: cmd = ["ps", "-ww", "-o", "pid=", "-o", "ppid=", "-o", "args="] output = subprocess.check_output(cmd) except OSError as e: # Python 2-compatible FileNotFoundError. if e.errno != errno.ENOENT: raise raise PsNotAvailable("ps not found") except subprocess.CalledProcessError as e: # `ps` can return 1 if the process list is completely empty. # (sarugaku/shellingham#15) if not e.output.strip(): return {} raise if not isinstance(output, str): encoding = sys.getfilesystemencoding() or sys.getdefaultencoding() output = output.decode(encoding) processes = {} for line in output.split("\n"): try: pid, ppid, args = line.strip().split(None, 2) # XXX: This is not right, but we are really out of options. # ps does not offer a sane way to decode the argument display, # and this is "Good Enough" for obtaining shell names. Hopefully # people don't name their shell with a space, or have something # like "/usr/bin/xonsh is uber". (sarugaku/shellingham#14) args = tuple(a.strip() for a in args.split(" ")) except ValueError: continue processes[pid] = Process(args=args, pid=pid, ppid=ppid) return processes shellingham-1.4.0/tasks/000077500000000000000000000000001400574446600151465ustar00rootroot00000000000000shellingham-1.4.0/tasks/CHANGELOG.rst.jinja2000066400000000000000000000015401400574446600203430ustar00rootroot00000000000000{% for section in sections %} {% set underline = "-" %} {% if section %} {{section}} {{ underline * section|length }}{% set underline = "~" %} {% endif %} {% if sections[section] %} {% for category, val in definitions.items() if category in sections[section] and category != 'trivial' %} {{ definitions[category]['name'] }} {{ underline * definitions[category]['name']|length }} {% if definitions[category]['showcontent'] %} {% for text, values in sections[section][category]|dictsort(by='value') %} - {{ text }}{% if category != 'process' %} {{ values|sort|join(',\n ') }} {% endif %} {% endfor %} {% else %} - {{ sections[section][category]['']|sort|join(', ') }} {% endif %} {% if sections[section][category]|length == 0 %} No significant changes. {% else %} {% endif %} {% endfor %} {% else %} No significant changes. {% endif %} {% endfor %} shellingham-1.4.0/tasks/__init__.py000066400000000000000000000056021400574446600172620ustar00rootroot00000000000000import pathlib import shutil import subprocess import invoke import parver from towncrier._builder import ( find_fragments, render_fragments, split_fragments, ) from towncrier._settings import load_config ROOT = pathlib.Path(__file__).resolve().parent.parent INIT_PY = ROOT.joinpath('src', 'shellingham', '__init__.py') @invoke.task() def clean(ctx): """Clean previously built package artifacts. """ ctx.run(f'python setup.py clean') dist = ROOT.joinpath('dist') print(f'[clean] Removing {dist}') if dist.exists(): shutil.rmtree(str(dist)) def _read_version(): out = subprocess.check_output(['git', 'tag'], encoding='ascii') version = max(parver.Version.parse(v).normalize() for v in ( line.strip() for line in out.split('\n') ) if v) return version def _write_version(v): lines = [] with INIT_PY.open() as f: for line in f: if line.startswith('__version__ = '): line = f'__version__ = {repr(str(v))}\n' lines.append(line) with INIT_PY.open('w', newline='\n') as f: f.write(''.join(lines)) def _render_log(): """Totally tap into Towncrier internals to get an in-memory result. """ config = load_config(ROOT) definitions = config['types'] fragments, fragment_filenames = find_fragments( pathlib.Path(config['directory']).absolute(), config['sections'], None, definitions, ) rendered = render_fragments( pathlib.Path(config['template']).read_text(encoding='utf-8'), config['issue_format'], split_fragments(fragments, definitions), definitions, config['underlines'][1:], ) return rendered REL_TYPES = ('major', 'minor', 'patch',) def _bump_release(version, type_): if type_ not in REL_TYPES: raise ValueError(f'{type_} not in {REL_TYPES}') index = REL_TYPES.index(type_) next_version = version.base_version().bump_release(index) print(f'[bump] {version} -> {next_version}') return next_version PREBUMP = 2 # Default to next patch number. def _prebump(version): next_version = version.bump_release(PREBUMP).bump_dev() print(f'[bump] {version} -> {next_version}') return next_version @invoke.task(pre=[clean]) def release(ctx, type_, repo): """Make a new release. """ version = _read_version() version = _bump_release(version, type_) _write_version(version) # Needs to happen before Towncrier deletes fragment files. tag_content = _render_log() ctx.run('towncrier') ctx.run(f'git commit -am "Release {version}"') tag_content = tag_content.replace('"', '\\"') ctx.run(f'git tag -a {version} -m "Version {version}\n\n{tag_content}"') ctx.run(f'setl publish --repository="{repo}"') version = _prebump(version) _write_version(version) ctx.run(f'git commit -am "Prebump to {version}"') shellingham-1.4.0/tests/000077500000000000000000000000001400574446600151635ustar00rootroot00000000000000shellingham-1.4.0/tests/test_posix.py000066400000000000000000000046641400574446600177500ustar00rootroot00000000000000import os import pytest from shellingham import posix from shellingham.posix._core import Process class EnvironManager(object): def __init__(self): self.backup = {} self.changed = set() def patch(self, **kwargs): self.backup.update({ k: os.environ[k] for k in kwargs if k in os.environ }) self.changed.update(kwargs.keys()) os.environ.update(kwargs) def unpatch(self): for k in self.changed: try: v = self.backup[k] except KeyError: os.environ.pop(k, None) else: os.environ[k] = v @pytest.fixture() def environ(request): """Provide environment variable override, and restore on finalize. """ manager = EnvironManager() request.addfinalizer(manager.unpatch) return manager MAPPING_EXAMPLE_KEEGANCSMITH = { '1480': Process(pid='1480', ppid='1477', args=( '/Applications/iTerm.app/Contents/MacOS/iTerm2', '--server', 'login', '-fp', 'keegan', )), '1482': Process(pid='1482', ppid='1481', args=( '-bash', )), '1556': Process(pid='1556', ppid='1482', args=( 'screen', )), '1558': Process(pid='1558', ppid='1557', args=( '-/usr/local/bin/bash', )), '1706': Process(pid='1706', ppid='1558', args=( '/Applications/Emacs.app/Contents/MacOS/Emacs-x86_64-10_10', '-nw', )), '77061': Process(pid='77061', ppid='1706', args=( '/usr/local/bin/aspell', '-a', '-m', '-B', '--encoding=utf-8', )), '1562': Process(pid='1562', ppid='1557', args=( '-/usr/local/bin/bash', )), '87033': Process(pid='87033', ppid='1557', args=( '-/usr/local/bin/bash', )), '84732': Process(pid='84732', ppid='1557', args=( '-/usr/local/bin/bash', )), '89065': Process(pid='89065', ppid='1557', args=( '-/usr/local/bin/bash', )), '80216': Process(pid='80216', ppid='1557', args=( '-/usr/local/bin/bash', )), } @pytest.mark.parametrize('mapping, result', [ ( # Based on pypa/pipenv#2496, provided by @keegancsmith. MAPPING_EXAMPLE_KEEGANCSMITH, ('bash', '==MOCKED=LOGIN=SHELL==/bash'), ), ]) def test_get_shell(mocker, environ, mapping, result): environ.patch(SHELL='==MOCKED=LOGIN=SHELL==/bash') mocker.patch.object(posix, '_get_process_mapping', return_value=mapping) assert posix.get_shell(pid=77061) == result