enderpy_python_type_checker/ruff_python_import_resolver/
py_typed.rs

1//! Support for [PEP 561] (`py.typed` files).
2//!
3//! [PEP 561]: https://peps.python.org/pep-0561/
4
5use std::path::{Path, PathBuf};
6
7#[derive(Debug, Clone, PartialEq, Eq)]
8pub(crate) struct PyTypedInfo {
9    /// The path to the `py.typed` file.
10    py_typed_path: PathBuf,
11
12    /// Whether the package is partially typed (as opposed to fully typed).
13    is_partially_typed: bool,
14}
15
16/// Returns the `py.typed` information for the given directory, if any.
17pub(crate) fn get_py_typed_info(dir_path: &Path) -> Option<PyTypedInfo> {
18    let py_typed_path = dir_path.join("py.typed");
19    if py_typed_path.is_file() {
20        // Do a quick sanity check on the size before we attempt to read it. This
21        // file should always be really small - typically zero bytes in length.
22        let file_len = py_typed_path.metadata().ok()?.len();
23        if file_len < 64 * 1024 {
24            // PEP 561 doesn't specify the format of "py.typed" in any detail other than
25            // to say that "If a stub package is partial it MUST include partial\n in a top
26            // level py.typed file."
27            let contents = std::fs::read_to_string(&py_typed_path).ok()?;
28            let is_partially_typed =
29                contents.contains("partial\n") || contents.contains("partial\r\n");
30            Some(PyTypedInfo {
31                py_typed_path,
32                is_partially_typed,
33            })
34        } else {
35            None
36        }
37    } else {
38        None
39    }
40}