├── .github └── workflows │ ├── release.yaml │ └── test.yaml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── examples ├── conf.py ├── main.py ├── nested │ ├── __init__.py │ ├── another.py │ └── exec_file └── some.py ├── pyproject.toml └── src ├── builtins.rs └── main.rs /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | release: 5 | types: [published] 6 | workflow_dispatch: 7 | 8 | concurrency: 9 | group: ${{ github.workflow }}-${{ github.ref_name }}-${{ github.sha }} 10 | cancel-in-progress: true 11 | 12 | jobs: 13 | build: 14 | name: Build on ${{ matrix.platform.os }} for ${{ matrix.platform.target }} 15 | runs-on: ${{ matrix.platform.os }} 16 | strategy: 17 | fail-fast: false 18 | matrix: 19 | platform: 20 | - os: ubuntu-latest 21 | target: x86_64 22 | - os: ubuntu-latest 23 | target: i686 24 | - os: ubuntu-latest 25 | target: aarch64 26 | - os: macos-latest 27 | target: x86_64 28 | args: --sdist 29 | - os: windows-latest 30 | target: x86_64 31 | steps: 32 | - uses: actions/checkout@v3 33 | - uses: dtolnay/rust-toolchain@stable 34 | with: 35 | toolchain: stable 36 | 37 | - uses: PyO3/maturin-action@v1 38 | with: 39 | target: ${{ matrix.platform.target }} 40 | manylinux: auto 41 | args: -b bin --release --out dist ${{ matrix.platform.args || '' }} 42 | 43 | - uses: actions/setup-python@v4 44 | with: 45 | python-version: "3.7" 46 | 47 | - name: Install built wheel on ${{ matrix.platform.target }} 48 | if: matrix.platform.target != 'i686' && matrix.platform.target != 'aarch64' 49 | shell: bash 50 | run: python -m pip install dist/third_party_imports-*.whl --force-reinstall 51 | 52 | - uses: uraimo/run-on-arch-action@v2.0.5 53 | if: matrix.platform.target == 'aarch64' 54 | name: Install built wheel on ${{ matrix.platform.target }} 55 | with: 56 | arch: ${{ matrix.platform.target }} 57 | distro: ubuntu20.04 58 | githubToken: ${{ github.token }} 59 | install: | 60 | apt-get update 61 | apt-get install -y --no-install-recommends python3 python3-pip 62 | pip3 install -U pip 63 | run: | 64 | python3 -m pip install third-party-imports --no-index --find-links dist/ --force-reinstall 65 | 66 | - name: Upload wheels 67 | uses: actions/upload-artifact@v3 68 | with: 69 | name: wheels 70 | path: dist 71 | 72 | release: 73 | name: Release 74 | runs-on: ubuntu-latest 75 | needs: build 76 | steps: 77 | - uses: actions/download-artifact@v2 78 | with: 79 | name: wheels 80 | - uses: actions/setup-python@v4 81 | - name: Publish to PyPI 82 | env: 83 | TWINE_USERNAME: __token__ 84 | TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }} 85 | run: | 86 | pip install --upgrade twine 87 | twine upload --skip-existing * 88 | -------------------------------------------------------------------------------- /.github/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | pull_request: 7 | branches: 8 | - main 9 | 10 | concurrency: 11 | group: ${{ github.workflow }}-${{ github.ref_name }}-${{ github.event.pull_request.number || github.sha }} 12 | cancel-in-progress: true 13 | 14 | jobs: 15 | build: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/checkout@v3 19 | - uses: actions-rs/toolchain@v1 20 | with: 21 | profile: minimal 22 | toolchain: nightly 23 | override: true 24 | - uses: Swatinem/rust-cache@v1 25 | - run: cargo build 26 | 27 | lint: 28 | runs-on: ubuntu-latest 29 | steps: 30 | - uses: actions/checkout@v3 31 | - uses: actions-rs/toolchain@v1 32 | with: 33 | profile: minimal 34 | toolchain: nightly 35 | override: true 36 | components: rustfmt 37 | - run: cargo fmt 38 | 39 | test: 40 | runs-on: ${{ matrix.os }} 41 | strategy: 42 | matrix: 43 | os: 44 | - ubuntu-latest 45 | - macos-latest 46 | - windows-latest 47 | steps: 48 | - uses: actions/checkout@v3 49 | - uses: actions-rs/toolchain@v1 50 | with: 51 | profile: minimal 52 | toolchain: nightly 53 | override: true 54 | - uses: Swatinem/rust-cache@v1 55 | - run: cargo test 56 | 57 | maturin-build: 58 | name: "maturin build" 59 | runs-on: ubuntu-latest 60 | steps: 61 | - uses: actions/checkout@v3 62 | - uses: actions-rs/toolchain@v1 63 | with: 64 | profile: minimal 65 | toolchain: nightly 66 | override: true 67 | - uses: Swatinem/rust-cache@v1 68 | - uses: actions/setup-python@v4 69 | with: 70 | python-version: "3.11" 71 | - run: pip install maturin 72 | - run: maturin build -b bin 73 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | 3 | # Byte-compiled / optimized / DLL files 4 | __pycache__/ 5 | .pytest_cache/ 6 | *.py[cod] 7 | 8 | # C extensions 9 | *.so 10 | 11 | # Distribution / packaging 12 | .Python 13 | .venv/ 14 | env/ 15 | bin/ 16 | build/ 17 | develop-eggs/ 18 | dist/ 19 | eggs/ 20 | lib/ 21 | lib64/ 22 | parts/ 23 | sdist/ 24 | var/ 25 | include/ 26 | man/ 27 | venv/ 28 | *.egg-info/ 29 | .installed.cfg 30 | *.egg 31 | 32 | # Installer logs 33 | pip-log.txt 34 | pip-delete-this-directory.txt 35 | pip-selfcheck.json 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .cache 42 | nosetests.xml 43 | coverage.xml 44 | 45 | # Translations 46 | *.mo 47 | 48 | # Mr Developer 49 | .mr.developer.cfg 50 | .project 51 | .pydevproject 52 | 53 | # Rope 54 | .ropeproject 55 | 56 | # Django stuff: 57 | *.log 58 | *.pot 59 | 60 | .DS_Store 61 | 62 | # Sphinx documentation 63 | docs/_build/ 64 | 65 | # PyCharm 66 | .idea/ 67 | 68 | # VSCode 69 | .vscode/ 70 | 71 | # Pyenv 72 | .python-version 73 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "ahash" 7 | version = "0.7.6" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" 10 | dependencies = [ 11 | "getrandom", 12 | "once_cell", 13 | "version_check", 14 | ] 15 | 16 | [[package]] 17 | name = "aho-corasick" 18 | version = "0.7.20" 19 | source = "registry+https://github.com/rust-lang/crates.io-index" 20 | checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" 21 | dependencies = [ 22 | "memchr", 23 | ] 24 | 25 | [[package]] 26 | name = "anyhow" 27 | version = "1.0.68" 28 | source = "registry+https://github.com/rust-lang/crates.io-index" 29 | checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61" 30 | 31 | [[package]] 32 | name = "ascii-canvas" 33 | version = "3.0.0" 34 | source = "registry+https://github.com/rust-lang/crates.io-index" 35 | checksum = "8824ecca2e851cec16968d54a01dd372ef8f95b244fb84b84e70128be347c3c6" 36 | dependencies = [ 37 | "term", 38 | ] 39 | 40 | [[package]] 41 | name = "atty" 42 | version = "0.2.14" 43 | source = "registry+https://github.com/rust-lang/crates.io-index" 44 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 45 | dependencies = [ 46 | "hermit-abi 0.1.19", 47 | "libc", 48 | "winapi", 49 | ] 50 | 51 | [[package]] 52 | name = "autocfg" 53 | version = "1.1.0" 54 | source = "registry+https://github.com/rust-lang/crates.io-index" 55 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 56 | 57 | [[package]] 58 | name = "bincode" 59 | version = "1.3.3" 60 | source = "registry+https://github.com/rust-lang/crates.io-index" 61 | checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" 62 | dependencies = [ 63 | "serde", 64 | ] 65 | 66 | [[package]] 67 | name = "bit-set" 68 | version = "0.5.3" 69 | source = "registry+https://github.com/rust-lang/crates.io-index" 70 | checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" 71 | dependencies = [ 72 | "bit-vec", 73 | ] 74 | 75 | [[package]] 76 | name = "bit-vec" 77 | version = "0.6.3" 78 | source = "registry+https://github.com/rust-lang/crates.io-index" 79 | checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" 80 | 81 | [[package]] 82 | name = "bitflags" 83 | version = "1.3.2" 84 | source = "registry+https://github.com/rust-lang/crates.io-index" 85 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 86 | 87 | [[package]] 88 | name = "bstr" 89 | version = "0.2.17" 90 | source = "registry+https://github.com/rust-lang/crates.io-index" 91 | checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" 92 | dependencies = [ 93 | "lazy_static", 94 | "memchr", 95 | "regex-automata", 96 | ] 97 | 98 | [[package]] 99 | name = "cc" 100 | version = "1.0.78" 101 | source = "registry+https://github.com/rust-lang/crates.io-index" 102 | checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d" 103 | 104 | [[package]] 105 | name = "cfg-if" 106 | version = "1.0.0" 107 | source = "registry+https://github.com/rust-lang/crates.io-index" 108 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 109 | 110 | [[package]] 111 | name = "clap" 112 | version = "4.1.1" 113 | source = "registry+https://github.com/rust-lang/crates.io-index" 114 | checksum = "4ec7a4128863c188deefe750ac1d1dfe66c236909f845af04beed823638dc1b2" 115 | dependencies = [ 116 | "bitflags", 117 | "clap_derive", 118 | "clap_lex", 119 | "is-terminal", 120 | "once_cell", 121 | "strsim", 122 | "termcolor", 123 | ] 124 | 125 | [[package]] 126 | name = "clap_derive" 127 | version = "4.1.0" 128 | source = "registry+https://github.com/rust-lang/crates.io-index" 129 | checksum = "684a277d672e91966334af371f1a7b5833f9aa00b07c84e92fbce95e00208ce8" 130 | dependencies = [ 131 | "heck", 132 | "proc-macro-error", 133 | "proc-macro2", 134 | "quote", 135 | "syn", 136 | ] 137 | 138 | [[package]] 139 | name = "clap_lex" 140 | version = "0.3.1" 141 | source = "registry+https://github.com/rust-lang/crates.io-index" 142 | checksum = "783fe232adfca04f90f56201b26d79682d4cd2625e0bc7290b95123afe558ade" 143 | dependencies = [ 144 | "os_str_bytes", 145 | ] 146 | 147 | [[package]] 148 | name = "crossbeam" 149 | version = "0.8.2" 150 | source = "registry+https://github.com/rust-lang/crates.io-index" 151 | checksum = "2801af0d36612ae591caa9568261fddce32ce6e08a7275ea334a06a4ad021a2c" 152 | dependencies = [ 153 | "cfg-if", 154 | "crossbeam-channel", 155 | "crossbeam-deque", 156 | "crossbeam-epoch", 157 | "crossbeam-queue", 158 | "crossbeam-utils", 159 | ] 160 | 161 | [[package]] 162 | name = "crossbeam-channel" 163 | version = "0.5.6" 164 | source = "registry+https://github.com/rust-lang/crates.io-index" 165 | checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521" 166 | dependencies = [ 167 | "cfg-if", 168 | "crossbeam-utils", 169 | ] 170 | 171 | [[package]] 172 | name = "crossbeam-deque" 173 | version = "0.8.2" 174 | source = "registry+https://github.com/rust-lang/crates.io-index" 175 | checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc" 176 | dependencies = [ 177 | "cfg-if", 178 | "crossbeam-epoch", 179 | "crossbeam-utils", 180 | ] 181 | 182 | [[package]] 183 | name = "crossbeam-epoch" 184 | version = "0.9.13" 185 | source = "registry+https://github.com/rust-lang/crates.io-index" 186 | checksum = "01a9af1f4c2ef74bb8aa1f7e19706bc72d03598c8a570bb5de72243c7a9d9d5a" 187 | dependencies = [ 188 | "autocfg", 189 | "cfg-if", 190 | "crossbeam-utils", 191 | "memoffset", 192 | "scopeguard", 193 | ] 194 | 195 | [[package]] 196 | name = "crossbeam-queue" 197 | version = "0.3.8" 198 | source = "registry+https://github.com/rust-lang/crates.io-index" 199 | checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add" 200 | dependencies = [ 201 | "cfg-if", 202 | "crossbeam-utils", 203 | ] 204 | 205 | [[package]] 206 | name = "crossbeam-utils" 207 | version = "0.8.14" 208 | source = "registry+https://github.com/rust-lang/crates.io-index" 209 | checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f" 210 | dependencies = [ 211 | "cfg-if", 212 | ] 213 | 214 | [[package]] 215 | name = "crunchy" 216 | version = "0.2.2" 217 | source = "registry+https://github.com/rust-lang/crates.io-index" 218 | checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" 219 | 220 | [[package]] 221 | name = "diff" 222 | version = "0.1.13" 223 | source = "registry+https://github.com/rust-lang/crates.io-index" 224 | checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" 225 | 226 | [[package]] 227 | name = "dirs-next" 228 | version = "2.0.0" 229 | source = "registry+https://github.com/rust-lang/crates.io-index" 230 | checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" 231 | dependencies = [ 232 | "cfg-if", 233 | "dirs-sys-next", 234 | ] 235 | 236 | [[package]] 237 | name = "dirs-sys-next" 238 | version = "0.1.2" 239 | source = "registry+https://github.com/rust-lang/crates.io-index" 240 | checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" 241 | dependencies = [ 242 | "libc", 243 | "redox_users", 244 | "winapi", 245 | ] 246 | 247 | [[package]] 248 | name = "either" 249 | version = "1.8.0" 250 | source = "registry+https://github.com/rust-lang/crates.io-index" 251 | checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" 252 | 253 | [[package]] 254 | name = "ena" 255 | version = "0.14.0" 256 | source = "registry+https://github.com/rust-lang/crates.io-index" 257 | checksum = "d7402b94a93c24e742487327a7cd839dc9d36fec9de9fb25b09f2dae459f36c3" 258 | dependencies = [ 259 | "log", 260 | ] 261 | 262 | [[package]] 263 | name = "errno" 264 | version = "0.2.8" 265 | source = "registry+https://github.com/rust-lang/crates.io-index" 266 | checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" 267 | dependencies = [ 268 | "errno-dragonfly", 269 | "libc", 270 | "winapi", 271 | ] 272 | 273 | [[package]] 274 | name = "errno-dragonfly" 275 | version = "0.1.2" 276 | source = "registry+https://github.com/rust-lang/crates.io-index" 277 | checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" 278 | dependencies = [ 279 | "cc", 280 | "libc", 281 | ] 282 | 283 | [[package]] 284 | name = "fixedbitset" 285 | version = "0.4.2" 286 | source = "registry+https://github.com/rust-lang/crates.io-index" 287 | checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" 288 | 289 | [[package]] 290 | name = "getrandom" 291 | version = "0.2.8" 292 | source = "registry+https://github.com/rust-lang/crates.io-index" 293 | checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" 294 | dependencies = [ 295 | "cfg-if", 296 | "libc", 297 | "wasi", 298 | ] 299 | 300 | [[package]] 301 | name = "hashbrown" 302 | version = "0.12.3" 303 | source = "registry+https://github.com/rust-lang/crates.io-index" 304 | checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" 305 | 306 | [[package]] 307 | name = "heck" 308 | version = "0.4.0" 309 | source = "registry+https://github.com/rust-lang/crates.io-index" 310 | checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" 311 | 312 | [[package]] 313 | name = "hermit-abi" 314 | version = "0.1.19" 315 | source = "registry+https://github.com/rust-lang/crates.io-index" 316 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 317 | dependencies = [ 318 | "libc", 319 | ] 320 | 321 | [[package]] 322 | name = "hermit-abi" 323 | version = "0.2.6" 324 | source = "registry+https://github.com/rust-lang/crates.io-index" 325 | checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" 326 | dependencies = [ 327 | "libc", 328 | ] 329 | 330 | [[package]] 331 | name = "indexmap" 332 | version = "1.9.2" 333 | source = "registry+https://github.com/rust-lang/crates.io-index" 334 | checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" 335 | dependencies = [ 336 | "autocfg", 337 | "hashbrown", 338 | ] 339 | 340 | [[package]] 341 | name = "io-lifetimes" 342 | version = "1.0.4" 343 | source = "registry+https://github.com/rust-lang/crates.io-index" 344 | checksum = "e7d6c6f8c91b4b9ed43484ad1a938e393caf35960fce7f82a040497207bd8e9e" 345 | dependencies = [ 346 | "libc", 347 | "windows-sys", 348 | ] 349 | 350 | [[package]] 351 | name = "is-terminal" 352 | version = "0.4.2" 353 | source = "registry+https://github.com/rust-lang/crates.io-index" 354 | checksum = "28dfb6c8100ccc63462345b67d1bbc3679177c75ee4bf59bf29c8b1d110b8189" 355 | dependencies = [ 356 | "hermit-abi 0.2.6", 357 | "io-lifetimes", 358 | "rustix", 359 | "windows-sys", 360 | ] 361 | 362 | [[package]] 363 | name = "itertools" 364 | version = "0.10.5" 365 | source = "registry+https://github.com/rust-lang/crates.io-index" 366 | checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" 367 | dependencies = [ 368 | "either", 369 | ] 370 | 371 | [[package]] 372 | name = "jwalk" 373 | version = "0.8.1" 374 | source = "registry+https://github.com/rust-lang/crates.io-index" 375 | checksum = "2735847566356cd2179a2a38264839308f7079fa96e6bd5a42d740460e003c56" 376 | dependencies = [ 377 | "crossbeam", 378 | "rayon", 379 | ] 380 | 381 | [[package]] 382 | name = "lalrpop" 383 | version = "0.19.8" 384 | source = "registry+https://github.com/rust-lang/crates.io-index" 385 | checksum = "b30455341b0e18f276fa64540aff54deafb54c589de6aca68659c63dd2d5d823" 386 | dependencies = [ 387 | "ascii-canvas", 388 | "atty", 389 | "bit-set", 390 | "diff", 391 | "ena", 392 | "itertools", 393 | "lalrpop-util", 394 | "petgraph", 395 | "pico-args", 396 | "regex", 397 | "regex-syntax", 398 | "string_cache", 399 | "term", 400 | "tiny-keccak", 401 | "unicode-xid", 402 | ] 403 | 404 | [[package]] 405 | name = "lalrpop-util" 406 | version = "0.19.8" 407 | source = "registry+https://github.com/rust-lang/crates.io-index" 408 | checksum = "bcf796c978e9b4d983414f4caedc9273aa33ee214c5b887bd55fde84c85d2dc4" 409 | dependencies = [ 410 | "regex", 411 | ] 412 | 413 | [[package]] 414 | name = "lazy_static" 415 | version = "1.4.0" 416 | source = "registry+https://github.com/rust-lang/crates.io-index" 417 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 418 | 419 | [[package]] 420 | name = "libc" 421 | version = "0.2.139" 422 | source = "registry+https://github.com/rust-lang/crates.io-index" 423 | checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" 424 | 425 | [[package]] 426 | name = "linux-raw-sys" 427 | version = "0.1.4" 428 | source = "registry+https://github.com/rust-lang/crates.io-index" 429 | checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" 430 | 431 | [[package]] 432 | name = "lock_api" 433 | version = "0.4.9" 434 | source = "registry+https://github.com/rust-lang/crates.io-index" 435 | checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" 436 | dependencies = [ 437 | "autocfg", 438 | "scopeguard", 439 | ] 440 | 441 | [[package]] 442 | name = "log" 443 | version = "0.4.17" 444 | source = "registry+https://github.com/rust-lang/crates.io-index" 445 | checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" 446 | dependencies = [ 447 | "cfg-if", 448 | ] 449 | 450 | [[package]] 451 | name = "lz4_flex" 452 | version = "0.9.5" 453 | source = "registry+https://github.com/rust-lang/crates.io-index" 454 | checksum = "1a8cbbb2831780bc3b9c15a41f5b49222ef756b6730a95f3decfdd15903eb5a3" 455 | dependencies = [ 456 | "twox-hash", 457 | ] 458 | 459 | [[package]] 460 | name = "memchr" 461 | version = "2.5.0" 462 | source = "registry+https://github.com/rust-lang/crates.io-index" 463 | checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" 464 | 465 | [[package]] 466 | name = "memoffset" 467 | version = "0.7.1" 468 | source = "registry+https://github.com/rust-lang/crates.io-index" 469 | checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" 470 | dependencies = [ 471 | "autocfg", 472 | ] 473 | 474 | [[package]] 475 | name = "new_debug_unreachable" 476 | version = "1.0.4" 477 | source = "registry+https://github.com/rust-lang/crates.io-index" 478 | checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" 479 | 480 | [[package]] 481 | name = "num-bigint" 482 | version = "0.4.3" 483 | source = "registry+https://github.com/rust-lang/crates.io-index" 484 | checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" 485 | dependencies = [ 486 | "autocfg", 487 | "num-integer", 488 | "num-traits", 489 | "serde", 490 | ] 491 | 492 | [[package]] 493 | name = "num-complex" 494 | version = "0.4.2" 495 | source = "registry+https://github.com/rust-lang/crates.io-index" 496 | checksum = "7ae39348c8bc5fbd7f40c727a9925f03517afd2ab27d46702108b6a7e5414c19" 497 | dependencies = [ 498 | "num-traits", 499 | "serde", 500 | ] 501 | 502 | [[package]] 503 | name = "num-integer" 504 | version = "0.1.45" 505 | source = "registry+https://github.com/rust-lang/crates.io-index" 506 | checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" 507 | dependencies = [ 508 | "autocfg", 509 | "num-traits", 510 | ] 511 | 512 | [[package]] 513 | name = "num-traits" 514 | version = "0.2.15" 515 | source = "registry+https://github.com/rust-lang/crates.io-index" 516 | checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" 517 | dependencies = [ 518 | "autocfg", 519 | ] 520 | 521 | [[package]] 522 | name = "num_cpus" 523 | version = "1.15.0" 524 | source = "registry+https://github.com/rust-lang/crates.io-index" 525 | checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" 526 | dependencies = [ 527 | "hermit-abi 0.2.6", 528 | "libc", 529 | ] 530 | 531 | [[package]] 532 | name = "once_cell" 533 | version = "1.17.0" 534 | source = "registry+https://github.com/rust-lang/crates.io-index" 535 | checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" 536 | 537 | [[package]] 538 | name = "os_str_bytes" 539 | version = "6.4.1" 540 | source = "registry+https://github.com/rust-lang/crates.io-index" 541 | checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" 542 | 543 | [[package]] 544 | name = "parking_lot" 545 | version = "0.12.1" 546 | source = "registry+https://github.com/rust-lang/crates.io-index" 547 | checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" 548 | dependencies = [ 549 | "lock_api", 550 | "parking_lot_core", 551 | ] 552 | 553 | [[package]] 554 | name = "parking_lot_core" 555 | version = "0.9.6" 556 | source = "registry+https://github.com/rust-lang/crates.io-index" 557 | checksum = "ba1ef8814b5c993410bb3adfad7a5ed269563e4a2f90c41f5d85be7fb47133bf" 558 | dependencies = [ 559 | "cfg-if", 560 | "libc", 561 | "redox_syscall", 562 | "smallvec", 563 | "windows-sys", 564 | ] 565 | 566 | [[package]] 567 | name = "petgraph" 568 | version = "0.6.2" 569 | source = "registry+https://github.com/rust-lang/crates.io-index" 570 | checksum = "e6d5014253a1331579ce62aa67443b4a658c5e7dd03d4bc6d302b94474888143" 571 | dependencies = [ 572 | "fixedbitset", 573 | "indexmap", 574 | ] 575 | 576 | [[package]] 577 | name = "phf" 578 | version = "0.10.1" 579 | source = "registry+https://github.com/rust-lang/crates.io-index" 580 | checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" 581 | dependencies = [ 582 | "phf_shared", 583 | ] 584 | 585 | [[package]] 586 | name = "phf_codegen" 587 | version = "0.10.0" 588 | source = "registry+https://github.com/rust-lang/crates.io-index" 589 | checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd" 590 | dependencies = [ 591 | "phf_generator", 592 | "phf_shared", 593 | ] 594 | 595 | [[package]] 596 | name = "phf_generator" 597 | version = "0.10.0" 598 | source = "registry+https://github.com/rust-lang/crates.io-index" 599 | checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" 600 | dependencies = [ 601 | "phf_shared", 602 | "rand", 603 | ] 604 | 605 | [[package]] 606 | name = "phf_shared" 607 | version = "0.10.0" 608 | source = "registry+https://github.com/rust-lang/crates.io-index" 609 | checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" 610 | dependencies = [ 611 | "siphasher", 612 | ] 613 | 614 | [[package]] 615 | name = "pico-args" 616 | version = "0.4.2" 617 | source = "registry+https://github.com/rust-lang/crates.io-index" 618 | checksum = "db8bcd96cb740d03149cbad5518db9fd87126a10ab519c011893b1754134c468" 619 | 620 | [[package]] 621 | name = "ppv-lite86" 622 | version = "0.2.17" 623 | source = "registry+https://github.com/rust-lang/crates.io-index" 624 | checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" 625 | 626 | [[package]] 627 | name = "precomputed-hash" 628 | version = "0.1.1" 629 | source = "registry+https://github.com/rust-lang/crates.io-index" 630 | checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" 631 | 632 | [[package]] 633 | name = "proc-macro-error" 634 | version = "1.0.4" 635 | source = "registry+https://github.com/rust-lang/crates.io-index" 636 | checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" 637 | dependencies = [ 638 | "proc-macro-error-attr", 639 | "proc-macro2", 640 | "quote", 641 | "syn", 642 | "version_check", 643 | ] 644 | 645 | [[package]] 646 | name = "proc-macro-error-attr" 647 | version = "1.0.4" 648 | source = "registry+https://github.com/rust-lang/crates.io-index" 649 | checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" 650 | dependencies = [ 651 | "proc-macro2", 652 | "quote", 653 | "version_check", 654 | ] 655 | 656 | [[package]] 657 | name = "proc-macro2" 658 | version = "1.0.49" 659 | source = "registry+https://github.com/rust-lang/crates.io-index" 660 | checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5" 661 | dependencies = [ 662 | "unicode-ident", 663 | ] 664 | 665 | [[package]] 666 | name = "quote" 667 | version = "1.0.23" 668 | source = "registry+https://github.com/rust-lang/crates.io-index" 669 | checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" 670 | dependencies = [ 671 | "proc-macro2", 672 | ] 673 | 674 | [[package]] 675 | name = "rand" 676 | version = "0.8.5" 677 | source = "registry+https://github.com/rust-lang/crates.io-index" 678 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 679 | dependencies = [ 680 | "libc", 681 | "rand_chacha", 682 | "rand_core", 683 | ] 684 | 685 | [[package]] 686 | name = "rand_chacha" 687 | version = "0.3.1" 688 | source = "registry+https://github.com/rust-lang/crates.io-index" 689 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 690 | dependencies = [ 691 | "ppv-lite86", 692 | "rand_core", 693 | ] 694 | 695 | [[package]] 696 | name = "rand_core" 697 | version = "0.6.4" 698 | source = "registry+https://github.com/rust-lang/crates.io-index" 699 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 700 | dependencies = [ 701 | "getrandom", 702 | ] 703 | 704 | [[package]] 705 | name = "rayon" 706 | version = "1.6.1" 707 | source = "registry+https://github.com/rust-lang/crates.io-index" 708 | checksum = "6db3a213adf02b3bcfd2d3846bb41cb22857d131789e01df434fb7e7bc0759b7" 709 | dependencies = [ 710 | "either", 711 | "rayon-core", 712 | ] 713 | 714 | [[package]] 715 | name = "rayon-core" 716 | version = "1.10.1" 717 | source = "registry+https://github.com/rust-lang/crates.io-index" 718 | checksum = "cac410af5d00ab6884528b4ab69d1e8e146e8d471201800fa1b4524126de6ad3" 719 | dependencies = [ 720 | "crossbeam-channel", 721 | "crossbeam-deque", 722 | "crossbeam-utils", 723 | "num_cpus", 724 | ] 725 | 726 | [[package]] 727 | name = "redox_syscall" 728 | version = "0.2.16" 729 | source = "registry+https://github.com/rust-lang/crates.io-index" 730 | checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" 731 | dependencies = [ 732 | "bitflags", 733 | ] 734 | 735 | [[package]] 736 | name = "redox_users" 737 | version = "0.4.3" 738 | source = "registry+https://github.com/rust-lang/crates.io-index" 739 | checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" 740 | dependencies = [ 741 | "getrandom", 742 | "redox_syscall", 743 | "thiserror", 744 | ] 745 | 746 | [[package]] 747 | name = "regex" 748 | version = "1.7.1" 749 | source = "registry+https://github.com/rust-lang/crates.io-index" 750 | checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" 751 | dependencies = [ 752 | "aho-corasick", 753 | "memchr", 754 | "regex-syntax", 755 | ] 756 | 757 | [[package]] 758 | name = "regex-automata" 759 | version = "0.1.10" 760 | source = "registry+https://github.com/rust-lang/crates.io-index" 761 | checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" 762 | 763 | [[package]] 764 | name = "regex-syntax" 765 | version = "0.6.28" 766 | source = "registry+https://github.com/rust-lang/crates.io-index" 767 | checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" 768 | 769 | [[package]] 770 | name = "rustc-hash" 771 | version = "1.1.0" 772 | source = "registry+https://github.com/rust-lang/crates.io-index" 773 | checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" 774 | 775 | [[package]] 776 | name = "rustix" 777 | version = "0.36.6" 778 | source = "registry+https://github.com/rust-lang/crates.io-index" 779 | checksum = "4feacf7db682c6c329c4ede12649cd36ecab0f3be5b7d74e6a20304725db4549" 780 | dependencies = [ 781 | "bitflags", 782 | "errno", 783 | "io-lifetimes", 784 | "libc", 785 | "linux-raw-sys", 786 | "windows-sys", 787 | ] 788 | 789 | [[package]] 790 | name = "rustpython-ast" 791 | version = "0.2.0" 792 | source = "registry+https://github.com/rust-lang/crates.io-index" 793 | checksum = "f7b83af38212db64ecfc76b6d53a1c6de89733236e39c69d4bc8ab565ae167b2" 794 | dependencies = [ 795 | "num-bigint", 796 | "rustpython-compiler-core", 797 | ] 798 | 799 | [[package]] 800 | name = "rustpython-compiler-core" 801 | version = "0.2.0" 802 | source = "registry+https://github.com/rust-lang/crates.io-index" 803 | checksum = "81870eb757460989d43ce41e069f1a01d0292b33011b23b0455d0a6394e4c77a" 804 | dependencies = [ 805 | "bincode", 806 | "bitflags", 807 | "bstr", 808 | "itertools", 809 | "lz4_flex", 810 | "num-bigint", 811 | "num-complex", 812 | "serde", 813 | "static_assertions", 814 | "thiserror", 815 | ] 816 | 817 | [[package]] 818 | name = "rustpython-parser" 819 | version = "0.2.0" 820 | source = "registry+https://github.com/rust-lang/crates.io-index" 821 | checksum = "db7348e148144ba85bc1284d97004062137306554fd00e7eb500d91720de5e78" 822 | dependencies = [ 823 | "ahash", 824 | "anyhow", 825 | "itertools", 826 | "lalrpop", 827 | "lalrpop-util", 828 | "log", 829 | "num-bigint", 830 | "num-traits", 831 | "phf", 832 | "phf_codegen", 833 | "rustc-hash", 834 | "rustpython-ast", 835 | "rustpython-compiler-core", 836 | "thiserror", 837 | "tiny-keccak", 838 | "unic-emoji-char", 839 | "unic-ucd-ident", 840 | "unicode_names2", 841 | ] 842 | 843 | [[package]] 844 | name = "rustversion" 845 | version = "1.0.11" 846 | source = "registry+https://github.com/rust-lang/crates.io-index" 847 | checksum = "5583e89e108996506031660fe09baa5011b9dd0341b89029313006d1fb508d70" 848 | 849 | [[package]] 850 | name = "scopeguard" 851 | version = "1.1.0" 852 | source = "registry+https://github.com/rust-lang/crates.io-index" 853 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 854 | 855 | [[package]] 856 | name = "serde" 857 | version = "1.0.152" 858 | source = "registry+https://github.com/rust-lang/crates.io-index" 859 | checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" 860 | dependencies = [ 861 | "serde_derive", 862 | ] 863 | 864 | [[package]] 865 | name = "serde_derive" 866 | version = "1.0.152" 867 | source = "registry+https://github.com/rust-lang/crates.io-index" 868 | checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" 869 | dependencies = [ 870 | "proc-macro2", 871 | "quote", 872 | "syn", 873 | ] 874 | 875 | [[package]] 876 | name = "siphasher" 877 | version = "0.3.10" 878 | source = "registry+https://github.com/rust-lang/crates.io-index" 879 | checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" 880 | 881 | [[package]] 882 | name = "smallvec" 883 | version = "1.10.0" 884 | source = "registry+https://github.com/rust-lang/crates.io-index" 885 | checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" 886 | 887 | [[package]] 888 | name = "static_assertions" 889 | version = "1.1.0" 890 | source = "registry+https://github.com/rust-lang/crates.io-index" 891 | checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" 892 | 893 | [[package]] 894 | name = "string_cache" 895 | version = "0.8.4" 896 | source = "registry+https://github.com/rust-lang/crates.io-index" 897 | checksum = "213494b7a2b503146286049378ce02b482200519accc31872ee8be91fa820a08" 898 | dependencies = [ 899 | "new_debug_unreachable", 900 | "once_cell", 901 | "parking_lot", 902 | "phf_shared", 903 | "precomputed-hash", 904 | ] 905 | 906 | [[package]] 907 | name = "strsim" 908 | version = "0.10.0" 909 | source = "registry+https://github.com/rust-lang/crates.io-index" 910 | checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" 911 | 912 | [[package]] 913 | name = "syn" 914 | version = "1.0.107" 915 | source = "registry+https://github.com/rust-lang/crates.io-index" 916 | checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" 917 | dependencies = [ 918 | "proc-macro2", 919 | "quote", 920 | "unicode-ident", 921 | ] 922 | 923 | [[package]] 924 | name = "term" 925 | version = "0.7.0" 926 | source = "registry+https://github.com/rust-lang/crates.io-index" 927 | checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" 928 | dependencies = [ 929 | "dirs-next", 930 | "rustversion", 931 | "winapi", 932 | ] 933 | 934 | [[package]] 935 | name = "termcolor" 936 | version = "1.2.0" 937 | source = "registry+https://github.com/rust-lang/crates.io-index" 938 | checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" 939 | dependencies = [ 940 | "winapi-util", 941 | ] 942 | 943 | [[package]] 944 | name = "third-party-imports" 945 | version = "0.0.7" 946 | dependencies = [ 947 | "clap", 948 | "jwalk", 949 | "rustpython-ast", 950 | "rustpython-parser", 951 | ] 952 | 953 | [[package]] 954 | name = "thiserror" 955 | version = "1.0.38" 956 | source = "registry+https://github.com/rust-lang/crates.io-index" 957 | checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" 958 | dependencies = [ 959 | "thiserror-impl", 960 | ] 961 | 962 | [[package]] 963 | name = "thiserror-impl" 964 | version = "1.0.38" 965 | source = "registry+https://github.com/rust-lang/crates.io-index" 966 | checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" 967 | dependencies = [ 968 | "proc-macro2", 969 | "quote", 970 | "syn", 971 | ] 972 | 973 | [[package]] 974 | name = "tiny-keccak" 975 | version = "2.0.2" 976 | source = "registry+https://github.com/rust-lang/crates.io-index" 977 | checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" 978 | dependencies = [ 979 | "crunchy", 980 | ] 981 | 982 | [[package]] 983 | name = "twox-hash" 984 | version = "1.6.3" 985 | source = "registry+https://github.com/rust-lang/crates.io-index" 986 | checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" 987 | dependencies = [ 988 | "cfg-if", 989 | "static_assertions", 990 | ] 991 | 992 | [[package]] 993 | name = "unic-char-property" 994 | version = "0.9.0" 995 | source = "registry+https://github.com/rust-lang/crates.io-index" 996 | checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" 997 | dependencies = [ 998 | "unic-char-range", 999 | ] 1000 | 1001 | [[package]] 1002 | name = "unic-char-range" 1003 | version = "0.9.0" 1004 | source = "registry+https://github.com/rust-lang/crates.io-index" 1005 | checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" 1006 | 1007 | [[package]] 1008 | name = "unic-common" 1009 | version = "0.9.0" 1010 | source = "registry+https://github.com/rust-lang/crates.io-index" 1011 | checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" 1012 | 1013 | [[package]] 1014 | name = "unic-emoji-char" 1015 | version = "0.9.0" 1016 | source = "registry+https://github.com/rust-lang/crates.io-index" 1017 | checksum = "0b07221e68897210270a38bde4babb655869637af0f69407f96053a34f76494d" 1018 | dependencies = [ 1019 | "unic-char-property", 1020 | "unic-char-range", 1021 | "unic-ucd-version", 1022 | ] 1023 | 1024 | [[package]] 1025 | name = "unic-ucd-ident" 1026 | version = "0.9.0" 1027 | source = "registry+https://github.com/rust-lang/crates.io-index" 1028 | checksum = "e230a37c0381caa9219d67cf063aa3a375ffed5bf541a452db16e744bdab6987" 1029 | dependencies = [ 1030 | "unic-char-property", 1031 | "unic-char-range", 1032 | "unic-ucd-version", 1033 | ] 1034 | 1035 | [[package]] 1036 | name = "unic-ucd-version" 1037 | version = "0.9.0" 1038 | source = "registry+https://github.com/rust-lang/crates.io-index" 1039 | checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" 1040 | dependencies = [ 1041 | "unic-common", 1042 | ] 1043 | 1044 | [[package]] 1045 | name = "unicode-ident" 1046 | version = "1.0.6" 1047 | source = "registry+https://github.com/rust-lang/crates.io-index" 1048 | checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" 1049 | 1050 | [[package]] 1051 | name = "unicode-xid" 1052 | version = "0.2.4" 1053 | source = "registry+https://github.com/rust-lang/crates.io-index" 1054 | checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" 1055 | 1056 | [[package]] 1057 | name = "unicode_names2" 1058 | version = "0.5.1" 1059 | source = "registry+https://github.com/rust-lang/crates.io-index" 1060 | checksum = "029df4cc8238cefc911704ff8fa210853a0f3bce2694d8f51181dd41ee0f3301" 1061 | 1062 | [[package]] 1063 | name = "version_check" 1064 | version = "0.9.4" 1065 | source = "registry+https://github.com/rust-lang/crates.io-index" 1066 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 1067 | 1068 | [[package]] 1069 | name = "wasi" 1070 | version = "0.11.0+wasi-snapshot-preview1" 1071 | source = "registry+https://github.com/rust-lang/crates.io-index" 1072 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 1073 | 1074 | [[package]] 1075 | name = "winapi" 1076 | version = "0.3.9" 1077 | source = "registry+https://github.com/rust-lang/crates.io-index" 1078 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 1079 | dependencies = [ 1080 | "winapi-i686-pc-windows-gnu", 1081 | "winapi-x86_64-pc-windows-gnu", 1082 | ] 1083 | 1084 | [[package]] 1085 | name = "winapi-i686-pc-windows-gnu" 1086 | version = "0.4.0" 1087 | source = "registry+https://github.com/rust-lang/crates.io-index" 1088 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 1089 | 1090 | [[package]] 1091 | name = "winapi-util" 1092 | version = "0.1.5" 1093 | source = "registry+https://github.com/rust-lang/crates.io-index" 1094 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 1095 | dependencies = [ 1096 | "winapi", 1097 | ] 1098 | 1099 | [[package]] 1100 | name = "winapi-x86_64-pc-windows-gnu" 1101 | version = "0.4.0" 1102 | source = "registry+https://github.com/rust-lang/crates.io-index" 1103 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1104 | 1105 | [[package]] 1106 | name = "windows-sys" 1107 | version = "0.42.0" 1108 | source = "registry+https://github.com/rust-lang/crates.io-index" 1109 | checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" 1110 | dependencies = [ 1111 | "windows_aarch64_gnullvm", 1112 | "windows_aarch64_msvc", 1113 | "windows_i686_gnu", 1114 | "windows_i686_msvc", 1115 | "windows_x86_64_gnu", 1116 | "windows_x86_64_gnullvm", 1117 | "windows_x86_64_msvc", 1118 | ] 1119 | 1120 | [[package]] 1121 | name = "windows_aarch64_gnullvm" 1122 | version = "0.42.1" 1123 | source = "registry+https://github.com/rust-lang/crates.io-index" 1124 | checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" 1125 | 1126 | [[package]] 1127 | name = "windows_aarch64_msvc" 1128 | version = "0.42.1" 1129 | source = "registry+https://github.com/rust-lang/crates.io-index" 1130 | checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" 1131 | 1132 | [[package]] 1133 | name = "windows_i686_gnu" 1134 | version = "0.42.1" 1135 | source = "registry+https://github.com/rust-lang/crates.io-index" 1136 | checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" 1137 | 1138 | [[package]] 1139 | name = "windows_i686_msvc" 1140 | version = "0.42.1" 1141 | source = "registry+https://github.com/rust-lang/crates.io-index" 1142 | checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" 1143 | 1144 | [[package]] 1145 | name = "windows_x86_64_gnu" 1146 | version = "0.42.1" 1147 | source = "registry+https://github.com/rust-lang/crates.io-index" 1148 | checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" 1149 | 1150 | [[package]] 1151 | name = "windows_x86_64_gnullvm" 1152 | version = "0.42.1" 1153 | source = "registry+https://github.com/rust-lang/crates.io-index" 1154 | checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" 1155 | 1156 | [[package]] 1157 | name = "windows_x86_64_msvc" 1158 | version = "0.42.1" 1159 | source = "registry+https://github.com/rust-lang/crates.io-index" 1160 | checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" 1161 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "third-party-imports" 3 | version = "0.0.7" 4 | edition = "2021" 5 | authors = ["Maksudul Haque "] 6 | homepage = "https://github.com/saadmk11/python-third-party-imports" 7 | repository = "https://github.com/saadmk11/python-third-party-imports" 8 | documentation = "https://github.com/saadmk11/python-third-party-imports" 9 | readme = "README.md" 10 | license = "MIT" 11 | 12 | [dependencies] 13 | clap = { version = "4.1.1", features = ["derive"] } 14 | jwalk = "0.8.1" 15 | rustpython-ast = "0.2.0" 16 | rustpython-parser = "0.2.0" 17 | 18 | [profile.release] 19 | codegen-units = 1 20 | opt-level = 3 21 | lto = "thin" 22 | panic = "abort" 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Maksudul Haque 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # python-third-party-imports 2 | 3 | This is a **Python** **CLI** tool built with **Rust** that finds all third-party packages imported into your Python project. 4 | 5 | # Install 6 | 7 | You can install this package via pip. 8 | 9 | ```console 10 | pip install third-party-imports 11 | ``` 12 | 13 | # Usage 14 | 15 | ### Run: 16 | 17 | **Check a directory:** 18 | 19 | ```console 20 | third-party-imports path/to/project/dir 21 | ``` 22 | 23 | **Check a directory with extra files:** 24 | 25 | ```console 26 | third-party-imports --extra-file-paths path/to/project/dir/foo.sh path/to/project/dir 27 | ``` 28 | 29 | **Note:** You can use `--extra-file-paths` option to include files that do not contain `.py` or `.pyi` extension. 30 | 31 | 32 | **Check a file:** 33 | 34 | ```console 35 | third-party-imports --project-root path/to/project/dir/ path/to/project/dir/foo/main.py 36 | ``` 37 | 38 | **Note:** If the file is not located in the project root directory, 39 | then you need to use `--project-root` option to specify where the project root directory is located. 40 | 41 | ### Help: 42 | 43 | ```console 44 | Find all third-party packages imported into your python project. 45 | 46 | Usage: third-party-imports [OPTIONS] 47 | 48 | Arguments: 49 | Path to a file or directory to check 50 | 51 | Options: 52 | -p, --project-root Path to the project's root directory 53 | -e, --extra-file-paths Extra file paths to check 54 | -h, --help Print help 55 | -V, --version Print version 56 | ``` 57 | 58 | # Example 59 | 60 | ```console 61 | third-party-imports examples/ 62 | ``` 63 | 64 | **Output:** 65 | 66 | ```console 67 | Found '4' third-party package imports in '5' files. (Took 920.50µs) 68 | 69 | celery 70 | django 71 | pandas 72 | requests 73 | ``` 74 | 75 | # Development 76 | 77 | ### Run using Cargo 78 | 79 | ```console 80 | cargo +nightly run -- path/to/project/dir 81 | ``` 82 | 83 | ### Code Format 84 | 85 | ```console 86 | cargo +nightly fmt 87 | ``` 88 | 89 | ### Run Tests 90 | 91 | ```console 92 | cargo +nightly test 93 | ``` 94 | 95 | ### Install Package in current `virtualenv` 96 | 97 | ```console 98 | maturin develop 99 | ``` 100 | 101 | # License 102 | 103 | The code in this project is released under the [MIT License](LICENSE). 104 | -------------------------------------------------------------------------------- /examples/conf.py: -------------------------------------------------------------------------------- 1 | def config(): 2 | ... 3 | -------------------------------------------------------------------------------- /examples/main.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | from uuid import UUID 4 | 5 | import nested.another 6 | import requests 7 | import some 8 | from django.db import models 9 | from nested import b 10 | from nested.another import a 11 | from some import func 12 | 13 | from .conf import config 14 | 15 | if 1 == 1: 16 | import if_package 17 | elif 2 == 2: 18 | import elif_package 19 | elif 2 == 2: 20 | import elif2_package 21 | else: 22 | import else_package 23 | 24 | 25 | def k(): 26 | import f_package 27 | 28 | 29 | class C: 30 | import c_package 31 | 32 | def d(): 33 | import m_package 34 | 35 | def j(): 36 | if 5 == 5: 37 | import m_if_package 38 | else: 39 | if 5 == 5: 40 | import nested_m_if_package 41 | 42 | 43 | try: 44 | import try_package 45 | except ModuleNotFoundError: 46 | import except_package 47 | 48 | if 8 == 8: 49 | import nested_if_except_package 50 | else: 51 | import try_else_package 52 | finally: 53 | import try_finally_package 54 | 55 | 56 | for i in range(5): 57 | import for_package 58 | else: 59 | import for_else_package 60 | 61 | 62 | with open("t") as f: 63 | import with_package 64 | 65 | while True: 66 | import while_package 67 | -------------------------------------------------------------------------------- /examples/nested/__init__.py: -------------------------------------------------------------------------------- 1 | def b(): 2 | ... 3 | -------------------------------------------------------------------------------- /examples/nested/another.py: -------------------------------------------------------------------------------- 1 | import urllib.parse 2 | import urllib.request 3 | 4 | import pandas as pd 5 | from celery import Celery 6 | from django.conf import settings 7 | 8 | 9 | def a(): 10 | ... 11 | -------------------------------------------------------------------------------- /examples/nested/exec_file: -------------------------------------------------------------------------------- 1 | #!#/usr/bin/python 2 | 3 | import time 4 | 5 | from selenium import webdriver 6 | from selenium.webdriver.common.keys import Keys 7 | -------------------------------------------------------------------------------- /examples/some.py: -------------------------------------------------------------------------------- 1 | def func(): 2 | ... 3 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["maturin>=0.14,<0.15"] 3 | build-backend = "maturin" 4 | 5 | [project] 6 | name = "third-party-imports" 7 | description = "Find all third-party packages imported into your python project." 8 | requires-python = ">=3.7" 9 | license = { file = "LICENSE" } 10 | classifiers = [ 11 | "Development Status :: 3 - Alpha", 12 | "Programming Language :: Python :: Implementation :: CPython", 13 | "Programming Language :: Python", 14 | "Programming Language :: Python :: 3.7", 15 | "Programming Language :: Python :: 3.8", 16 | "Programming Language :: Python :: 3.9", 17 | "Programming Language :: Python :: 3.10", 18 | "Programming Language :: Python :: 3.11", 19 | "Environment :: Console", 20 | "Intended Audience :: Developers", 21 | "License :: OSI Approved :: MIT License", 22 | ] 23 | authors = [ 24 | { name = "Maksudul Haque", email = "saad.mk112@gmail.com" }, 25 | ] 26 | maintainers = [ 27 | { name = "Maksudul Haque", email = "saad.mk112@gmail.com" }, 28 | ] 29 | keywords = ["import", "finder"] 30 | urls = { repository = "https://github.com/saadmk11/python-third-party-imports" } 31 | 32 | 33 | [tool.maturin] 34 | bindings = "bin" 35 | strip = true 36 | -------------------------------------------------------------------------------- /src/builtins.rs: -------------------------------------------------------------------------------- 1 | // Taken from https://pycqa.github.io/isort/ 2 | 3 | pub static STANDARD_LIBRARY: &[&str; 327] = &[ 4 | "__future__", 5 | "winsound", 6 | "zoneinfo", 7 | "gl", 8 | "UserList", 9 | "sre_parse", 10 | "colorsys", 11 | "plistlib", 12 | "tomllib", 13 | "difflib", 14 | "imputil", 15 | "gettext", 16 | "timeit", 17 | "io", 18 | "sys", 19 | "modulefinder", 20 | "poplib", 21 | "sre_compile", 22 | "unittest", 23 | "zipapp", 24 | "zlib", 25 | "runpy", 26 | "wave", 27 | "linecache", 28 | "sysconfig", 29 | "imp", 30 | "asynchat", 31 | "compileall", 32 | "macostools", 33 | "dircache", 34 | "md5", 35 | "PixMapWrapper", 36 | "SimpleXMLRPCServer", 37 | "sqlite3", 38 | "dummy_threading", 39 | "mmap", 40 | "dis", 41 | "types", 42 | "code", 43 | "ScrolledText", 44 | "weakref", 45 | "msvcrt", 46 | "jpeg", 47 | "webbrowser", 48 | "exceptions", 49 | "selectors", 50 | "hashlib", 51 | "pdb", 52 | "anydbm", 53 | "ctypes", 54 | "gzip", 55 | "string", 56 | "CGIHTTPServer", 57 | "unicodedata", 58 | "binascii", 59 | "operator", 60 | "faulthandler", 61 | "optparse", 62 | "videoreader", 63 | "uu", 64 | "popen2", 65 | "parser", 66 | "ConfigParser", 67 | "msilib", 68 | "pwd", 69 | "SocketServer", 70 | "smtpd", 71 | "math", 72 | "enum", 73 | "user", 74 | "flp", 75 | "ColorPicker", 76 | "sets", 77 | "rlcompleter", 78 | "SUNAUDIODEV", 79 | "sre_constants", 80 | "cfmfile", 81 | "asyncio", 82 | "sched", 83 | "netrc", 84 | "DocXMLRPCServer", 85 | "syslog", 86 | "marshal", 87 | "gc", 88 | "mhlib", 89 | "contextvars", 90 | "functools", 91 | "ntpath", 92 | "BaseHTTPServer", 93 | "binhex", 94 | "imghdr", 95 | "macerrors", 96 | "symbol", 97 | "cd", 98 | "mailbox", 99 | "htmlentitydefs", 100 | "Carbon", 101 | "builtins", 102 | "platform", 103 | "reprlib", 104 | "cgi", 105 | "fcntl", 106 | "codeop", 107 | "socket", 108 | "zipfile", 109 | "Queue", 110 | "lib2to3", 111 | "Tix", 112 | "tempfile", 113 | "keyword", 114 | "json", 115 | "sunaudiodev", 116 | "_winreg", 117 | "cPickle", 118 | "dataclasses", 119 | "pty", 120 | "pydoc", 121 | "audioop", 122 | "warnings", 123 | "fileinput", 124 | "datetime", 125 | "bdb", 126 | "py_compile", 127 | "getpass", 128 | "cProfile", 129 | "queue", 130 | "applesingle", 131 | "contextlib", 132 | "collections", 133 | "idlelib", 134 | "spwd", 135 | "pprint", 136 | "doctest", 137 | "Tkinter", 138 | "shutil", 139 | "email", 140 | "subprocess", 141 | "traceback", 142 | "profile", 143 | "aetypes", 144 | "cmd", 145 | "dl", 146 | "shelve", 147 | "cStringIO", 148 | "pstats", 149 | "pkgutil", 150 | "FrameWork", 151 | "tty", 152 | "xml", 153 | "cookielib", 154 | "decimal", 155 | "distutils", 156 | "DEVICE", 157 | "socketserver", 158 | "calendar", 159 | "Cookie", 160 | "argparse", 161 | "posix", 162 | "select", 163 | "ossaudiodev", 164 | "rexec", 165 | "imaplib", 166 | "dumbdbm", 167 | "findertools", 168 | "new", 169 | "hotshot", 170 | "MacOS", 171 | "future_builtins", 172 | "pipes", 173 | "compiler", 174 | "urllib", 175 | "icopen", 176 | "tabnanny", 177 | "_thread", 178 | "pickletools", 179 | "MiniAEFrame", 180 | "numbers", 181 | "fpformat", 182 | "concurrent", 183 | "random", 184 | "mailcap", 185 | "codecs", 186 | "symtable", 187 | "zipimport", 188 | "EasyDialogs", 189 | "tkinter", 190 | "configparser", 191 | "encodings", 192 | "imgfile", 193 | "UserDict", 194 | "time", 195 | "turtledemo", 196 | "typing", 197 | "rfc822", 198 | "SimpleHTTPServer", 199 | "fnmatch", 200 | "sgmllib", 201 | "whichdb", 202 | "ftplib", 203 | "readline", 204 | "venv", 205 | "mutex", 206 | "inspect", 207 | "HTMLParser", 208 | "UserString", 209 | "multiprocessing", 210 | "commands", 211 | "dummy_thread", 212 | "posixpath", 213 | "threading", 214 | "W", 215 | "tarfile", 216 | "http", 217 | "textwrap", 218 | "abc", 219 | "statistics", 220 | "stat", 221 | "ic", 222 | "uuid", 223 | "copyreg", 224 | "_dummy_thread", 225 | "AL", 226 | "urlparse", 227 | "autoGIL", 228 | "sre", 229 | "dbm", 230 | "macpath", 231 | "wsgiref", 232 | "smtplib", 233 | "os", 234 | "Bastion", 235 | "imageop", 236 | "array", 237 | "xmlrpclib", 238 | "gensuitemodule", 239 | "xmlrpc", 240 | "dbhash", 241 | "crypt", 242 | "token", 243 | "fractions", 244 | "fl", 245 | "graphlib", 246 | "nntplib", 247 | "aepack", 248 | "fm", 249 | "ipaddress", 250 | "test", 251 | "sndhdr", 252 | "al", 253 | "shlex", 254 | "mimify", 255 | "formatter", 256 | "__builtin__", 257 | "site", 258 | "re", 259 | "turtle", 260 | "xdrlib", 261 | "stringprep", 262 | "bisect", 263 | "errno", 264 | "multifile", 265 | "ensurepip", 266 | "ssl", 267 | "csv", 268 | "itertools", 269 | "sha", 270 | "winreg", 271 | "fpectl", 272 | "locale", 273 | "cgitb", 274 | "StringIO", 275 | "hmac", 276 | "htmllib", 277 | "base64", 278 | "lzma", 279 | "ast", 280 | "pyclbr", 281 | "gdbm", 282 | "telnetlib", 283 | "quopri", 284 | "aetools", 285 | "Nav", 286 | "aifc", 287 | "MimeWriter", 288 | "pickle", 289 | "trace", 290 | "copy", 291 | "secrets", 292 | "GL", 293 | "grp", 294 | "urllib2", 295 | "tokenize", 296 | "mimetools", 297 | "struct", 298 | "bsddb", 299 | "robotparser", 300 | "atexit", 301 | "curses", 302 | "FL", 303 | "pathlib", 304 | "mimetypes", 305 | "getopt", 306 | "html", 307 | "logging", 308 | "macresource", 309 | "statvfs", 310 | "termios", 311 | "signal", 312 | "chunk", 313 | "filecmp", 314 | "ttk", 315 | "bz2", 316 | "nis", 317 | "resource", 318 | "buildtools", 319 | "heapq", 320 | "_ast", 321 | "importlib", 322 | "tracemalloc", 323 | "glob", 324 | "sunau", 325 | "asyncore", 326 | "copy_reg", 327 | "httplib", 328 | "cmath", 329 | "posixfile", 330 | "thread", 331 | ]; 332 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashSet; 2 | use std::fs; 3 | use std::path::PathBuf; 4 | use std::sync::Arc; 5 | use std::thread; 6 | use std::time::Instant; 7 | 8 | use clap::Parser; 9 | use jwalk::{Parallelism, WalkDir}; 10 | use rustpython_ast::{AliasData, ExcepthandlerKind, Located}; 11 | use rustpython_parser::ast::StmtKind; 12 | use rustpython_parser::parser; 13 | 14 | use builtins::STANDARD_LIBRARY; 15 | 16 | mod builtins; 17 | 18 | #[derive(Debug, Parser)] 19 | #[command( 20 | author, 21 | about = "Find all third-party packages imported into your python project." 22 | )] 23 | #[command(version)] 24 | pub struct Arguments { 25 | /// Path to the project's root directory. 26 | #[arg(value_parser = project_root_value_parser, short, long)] 27 | pub project_root: Option, 28 | /// Path to a file or directory to check. 29 | #[arg(value_parser = path_value_parser)] 30 | pub path: PathBuf, 31 | /// Extra file paths to check 32 | #[arg(value_parser = extra_file_paths_value_parser, value_delimiter = ',', short, long)] 33 | pub extra_file_paths: Option>, 34 | } 35 | 36 | fn project_root_value_parser(arg: &str) -> Result { 37 | let path_buf = PathBuf::from(arg); 38 | 39 | if !path_buf.exists() { 40 | Err("Path does not exist".to_string()) 41 | } else if !path_buf.is_dir() { 42 | Err("Path must be a directory".to_string()) 43 | } else { 44 | Ok(path_buf) 45 | } 46 | } 47 | 48 | fn path_value_parser(arg: &str) -> Result { 49 | let path_buf = PathBuf::from(arg); 50 | 51 | if !path_buf.exists() { 52 | Err("Path does not exist".to_string()) 53 | } else { 54 | Ok(path_buf) 55 | } 56 | } 57 | 58 | fn extra_file_paths_value_parser(arg: &str) -> Result { 59 | let path_buf = PathBuf::from(arg); 60 | 61 | if !path_buf.exists() { 62 | Err("Path does not exist".to_string()) 63 | } else if !path_buf.is_file() { 64 | Err("Path must be a file".to_string()) 65 | } else { 66 | Ok(path_buf) 67 | } 68 | } 69 | 70 | pub fn main() { 71 | let now = Instant::now(); 72 | let args = Arguments::parse(); 73 | 74 | let project_root: PathBuf = match args.project_root { 75 | Some(root) => root, 76 | None => { 77 | if args.path.is_dir() { 78 | println!("Using `{}` as project root.", args.path.display()); 79 | args.path.clone() 80 | } else if let Some(parent) = args.path.parent() { 81 | println!("Using `{}` as project root.", parent.display()); 82 | parent.to_path_buf() 83 | } else { 84 | panic!("Could not find project root!") 85 | } 86 | } 87 | }; 88 | 89 | let (file_count, third_party_packages): (usize, HashSet) = 90 | run(project_root, &args.path, &args.extra_file_paths); 91 | 92 | println!( 93 | "Found '{}' third-party package imports in '{}' files. (Took {:.2?})\n", 94 | third_party_packages.len(), 95 | file_count, 96 | now.elapsed() 97 | ); 98 | third_party_packages.iter().for_each(|package| { 99 | println!("{package}"); 100 | }); 101 | } 102 | 103 | /// Traverse all the python files in project root and return 104 | /// all third-party package imported and the number of files parsed 105 | fn run( 106 | project_root: PathBuf, 107 | path: &PathBuf, 108 | extra_file_paths: &Option>, 109 | ) -> (usize, HashSet) { 110 | let mut third_party_packages: HashSet = HashSet::new(); 111 | let mut handles = Vec::new(); 112 | 113 | let project_root = Arc::new(project_root); 114 | 115 | for entry in WalkDir::new(path) 116 | .parallelism(Parallelism::RayonNewPool(0)) 117 | .into_iter() 118 | .filter_map(|e| e.ok()) 119 | .filter(|e| { 120 | e.path() 121 | .extension() 122 | .map(|ext| ext == "py" || ext == "pyi") 123 | .unwrap_or_default() 124 | }) 125 | { 126 | handles.push(spawn_thread(&project_root, entry.path())); 127 | } 128 | 129 | if let Some(extra_file_paths) = extra_file_paths { 130 | for path in extra_file_paths { 131 | handles.push(spawn_thread(&project_root, path.to_path_buf())); 132 | } 133 | } 134 | 135 | let file_count: usize = handles.len(); 136 | 137 | for handle in handles { 138 | if let Ok(Some(packages)) = handle.join() { 139 | third_party_packages.extend(packages); 140 | } 141 | } 142 | (file_count, third_party_packages) 143 | } 144 | 145 | /// Checks the given file in a new thread 146 | fn spawn_thread( 147 | project_root: &Arc, 148 | file_path: PathBuf, 149 | ) -> thread::JoinHandle>> { 150 | let root_path = Arc::clone(project_root); 151 | 152 | thread::spawn(move || -> Option> { 153 | let content = fs::read_to_string(&file_path).ok()?; 154 | 155 | if let Ok(python_ast) = parser::parse_program(&content, &file_path.to_string_lossy()) { 156 | Some(find_third_party_packages( 157 | &root_path, 158 | &file_path, 159 | &python_ast, 160 | )) 161 | } else { 162 | None 163 | } 164 | }) 165 | } 166 | 167 | /// Find third-party imports in a single python file 168 | fn find_third_party_packages( 169 | project_root: &PathBuf, 170 | file_path: &PathBuf, 171 | python_ast: &Vec>, 172 | ) -> HashSet { 173 | let mut third_party_packages: HashSet = HashSet::new(); 174 | 175 | for stmt in python_ast { 176 | parse_stmt(stmt, project_root, file_path, &mut third_party_packages); 177 | } 178 | third_party_packages 179 | } 180 | 181 | /// Recursively parse python ast and update `third_party_packages` 182 | /// if third-party import is found 183 | fn parse_stmt( 184 | stmt: &Located, 185 | project_root: &PathBuf, 186 | file_path: &PathBuf, 187 | third_party_packages: &mut HashSet, 188 | ) { 189 | match &stmt.node { 190 | StmtKind::Import { names } => { 191 | for name in names { 192 | let AliasData { name: module, .. } = &name.node; 193 | 194 | if let Some(module_base) = is_third_party_package(project_root, file_path, module) { 195 | third_party_packages.insert(module_base.to_string()); 196 | } 197 | } 198 | } 199 | StmtKind::ImportFrom { 200 | module: Some(module), 201 | names: _names, 202 | level: Some(level), 203 | } if *level == 0 => { 204 | if let Some(module_base) = is_third_party_package(project_root, file_path, module) { 205 | third_party_packages.insert(module_base.to_string()); 206 | } 207 | } 208 | StmtKind::AsyncFunctionDef { body, .. } 209 | | StmtKind::FunctionDef { body, .. } 210 | | StmtKind::ClassDef { body, .. } 211 | | StmtKind::AsyncWith { body, .. } 212 | | StmtKind::With { body, .. } => { 213 | for stmt in body { 214 | parse_stmt(stmt, project_root, file_path, third_party_packages); 215 | } 216 | } 217 | StmtKind::For { body, orelse, .. } 218 | | StmtKind::AsyncFor { body, orelse, .. } 219 | | StmtKind::While { body, orelse, .. } 220 | | StmtKind::If { body, orelse, .. } => { 221 | for stmt in body { 222 | parse_stmt(stmt, project_root, file_path, third_party_packages); 223 | } 224 | 225 | for stmt in orelse { 226 | parse_stmt(stmt, project_root, file_path, third_party_packages); 227 | } 228 | } 229 | StmtKind::Try { 230 | body, 231 | handlers, 232 | orelse, 233 | finalbody, 234 | } => { 235 | for stmt in body { 236 | parse_stmt(stmt, project_root, file_path, third_party_packages); 237 | } 238 | 239 | for excepthandler in handlers { 240 | let ExcepthandlerKind::ExceptHandler { body, .. } = &excepthandler.node; 241 | for stmt in body { 242 | parse_stmt(stmt, project_root, file_path, third_party_packages); 243 | } 244 | } 245 | 246 | for stmt in orelse { 247 | parse_stmt(stmt, project_root, file_path, third_party_packages); 248 | } 249 | 250 | for stmt in finalbody { 251 | parse_stmt(stmt, project_root, file_path, third_party_packages); 252 | } 253 | } 254 | _ => (), 255 | } 256 | } 257 | 258 | /// Check if the module is locally imported 259 | fn is_local_import(project_root: &PathBuf, file_path: &PathBuf, module: &str) -> bool { 260 | if let Ok(project_root_canonical) = project_root.canonicalize() { 261 | if project_root_canonical.ends_with(module) { 262 | return true; 263 | } 264 | } 265 | if project_root.join(module).is_dir() || project_root.join(format!("{module}.py")).is_file() { 266 | return true; 267 | } 268 | if let Some(parent) = file_path.parent() { 269 | if parent.join(module).is_dir() || parent.join(format!("{module}.py")).is_file() { 270 | return true; 271 | } 272 | } 273 | false 274 | } 275 | 276 | /// Check if the module is a third-party import or not 277 | /// return module base if it is otherwise return None 278 | fn is_third_party_package<'a>( 279 | project_root: &PathBuf, 280 | file_path: &PathBuf, 281 | module: &'a str, 282 | ) -> Option<&'a str> { 283 | let module_base = match module.split_once('.') { 284 | Some((first, _)) => first, 285 | None => module, 286 | }; 287 | 288 | if !STANDARD_LIBRARY.contains(&module_base) 289 | && !is_local_import(project_root, file_path, module_base) 290 | { 291 | Some(module_base) 292 | } else { 293 | None 294 | } 295 | } 296 | 297 | #[cfg(test)] 298 | mod tests { 299 | use super::*; 300 | 301 | #[test] 302 | fn test_find_third_party_packages() { 303 | let content = "import requests 304 | from uuid import UUID 305 | from . import another 306 | from .new import local 307 | from ..again import loc 308 | from django.http import ( 309 | Http404, 310 | JsonResponse, 311 | ) 312 | 313 | from .another import one, two, three 314 | import os, sys 315 | 316 | def f(): 317 | import pandas 318 | from .new import local 319 | print('test') 320 | "; 321 | let file_path = PathBuf::from("./t.py"); 322 | let root = PathBuf::from("."); 323 | let python_ast = parser::parse_program(content, &file_path.to_string_lossy()).unwrap(); 324 | assert_eq!( 325 | find_third_party_packages(&root, &file_path, &python_ast), 326 | HashSet::from([ 327 | "requests".to_string(), 328 | "django".to_string(), 329 | "pandas".to_string() 330 | ]) 331 | ); 332 | } 333 | 334 | #[test] 335 | fn test_run() { 336 | let root = PathBuf::from("./examples"); 337 | let path = PathBuf::from("./examples"); 338 | let extra_file_paths: Option> = 339 | Some(Vec::from([PathBuf::from("./examples/nested/exec_file")])); 340 | assert_eq!( 341 | run(root, &path, &extra_file_paths), 342 | ( 343 | 6, 344 | HashSet::from([ 345 | "pandas".to_string(), 346 | "else_package".to_string(), 347 | "if_package".to_string(), 348 | "except_package".to_string(), 349 | "c_package".to_string(), 350 | "for_package".to_string(), 351 | "f_package".to_string(), 352 | "nested_if_except_package".to_string(), 353 | "m_package".to_string(), 354 | "celery".to_string(), 355 | "requests".to_string(), 356 | "try_else_package".to_string(), 357 | "m_if_package".to_string(), 358 | "nested_m_if_package".to_string(), 359 | "try_finally_package".to_string(), 360 | "django".to_string(), 361 | "elif2_package".to_string(), 362 | "with_package".to_string(), 363 | "elif_package".to_string(), 364 | "for_else_package".to_string(), 365 | "while_package".to_string(), 366 | "try_package".to_string(), 367 | "selenium".to_string(), 368 | ]) 369 | ) 370 | ); 371 | } 372 | #[test] 373 | fn test_run_nested_path() { 374 | let root = PathBuf::from("./examples"); 375 | let path = PathBuf::from("./examples/nested"); 376 | let extra_file_paths: Option> = None; 377 | assert_eq!( 378 | run(root, &path, &extra_file_paths), 379 | ( 380 | 2, 381 | HashSet::from([ 382 | "pandas".to_string(), 383 | "celery".to_string(), 384 | "django".to_string(), 385 | ]) 386 | ) 387 | ); 388 | } 389 | #[test] 390 | fn test_run_single_file() { 391 | let root = PathBuf::from("./examples"); 392 | let path = PathBuf::from("./examples/nested/another.py"); 393 | let extra_file_paths: Option> = Some(Vec::new()); 394 | assert_eq!( 395 | run(root, &path, &extra_file_paths), 396 | ( 397 | 1, 398 | HashSet::from([ 399 | "pandas".to_string(), 400 | "celery".to_string(), 401 | "django".to_string(), 402 | ]) 403 | ) 404 | ); 405 | } 406 | } 407 | --------------------------------------------------------------------------------