Skip to content

A rust crate for parsing and analyzing the imports within a python package

License

Notifications You must be signed in to change notification settings

Peter554/pyimports

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

83 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

pyimports

CI Crates.io Docs

A rust crate for parsing and analyzing the imports within a python package.

Example

A short example (for more information refer to the docs):

use anyhow::Result;
use maplit::{hashmap,hashset};

use pyimports::prelude::*;
use pyimports::{PackageInfo,ImportsInfo,PackageItemToken};

// You shouldn't use `testpackage!`, it just creates a fake python package
// in a temporary directory. It's (unfortunately) included in the public API
// so that it can be used in the doctests.
use pyimports::{testpackage,TestPackage};

fn main() -> Result<()> {
    let testpackage = testpackage! {
        "__init__.py" => "from testpackage import a, b",
        "a.py" => "from testpackage import b",
        "b.py" => "from testpackage import c, d",
        "c.py" => "from testpackage import d",
        "d.py" => ""
    };
    let package_info = PackageInfo::build(testpackage.path())?;
    let imports_info = ImportsInfo::build(package_info)?;

    let item = |pypath: &str| -> Result<PackageItemToken> {
        Ok(imports_info.package_info().get_item_by_pypath(pypath)?.unwrap().token())
    };

    let root_pkg = item("testpackage")?;
    let root_init = item("testpackage.__init__")?;
    let a = item("testpackage.a")?;
    let b = item("testpackage.b")?;
    let c = item("testpackage.c")?;
    let d = item("testpackage.d")?;

    assert_eq!(
        imports_info.internal_imports().get_direct_imports(),
        hashmap! {
            root_pkg => hashset!{root_init},
            root_init => hashset!{a, b},
            a => hashset!{b},
            b => hashset!{c, d},
            c => hashset!{d},
            d => hashset!{},
        }
    );

    assert_eq!(
        imports_info.internal_imports().get_downstream_items(root_pkg)?,
        hashset! {root_init, a, b, c, d}
    );

    assert_eq!(
        imports_info.internal_imports().get_shortest_path(root_pkg, d)?,
        Some(vec![root_pkg, root_init, b, d])
    );

    Ok(())
}

Scope

This crate might be useful for something eventually, but right now it's mainly just a hobby project for me to learn about rust.

If you are looking for something more mature, try grimp/import-linter.

Limitations

The python parser used within this crate does not currently support python 3.12+ - see the related GitHub issue here.

Next steps

Some possible next steps that I may explore if/when I get time:

  • Fix issue with python parser, to support python 3.12+.
  • Performance benchmarking/improvements.
  • Python bindings (via maturin).
  • Higher level features e.g. import contracts, similar to import-linter.
  • Faster path calculations (via e.g. fast_paths).

About

A rust crate for parsing and analyzing the imports within a python package

Resources

License

Stars

Watchers

Forks

Languages