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}