A zero-dependency Rust parser for Python docstrings (Google / NumPy style).
Produces a unified syntax tree with byte-precise source locations on every token — designed as infrastructure for linters and formatters.
Python bindings are also available as pydocstring-rs.
- Full syntax tree — builds a complete AST, not just extracted fields; traverse it with the built-in
Visitor+walk - Typed nodes per style — style-specific accessors like
GoogleArg,NumPyParameterwith full type safety - Byte-precise source locations — every token carries its exact byte range for pinpoint diagnostics
- Zero dependencies — pure Rust, no external crates, no regex
- Error-resilient — never panics; malformed input still yields a best-effort tree
- Style auto-detection — hand it a docstring, it tells you the convention
[dependencies]
pydocstring = "0.0.3"use pydocstring::google::{parse_google, nodes::GoogleDocstring};
use pydocstring::GoogleSectionKind;
let input = "Summary.\n\nArgs:\n x (int): The value.\n y (int): Another value.";
let result = parse_google(input);
let doc = GoogleDocstring::cast(result.root()).unwrap();
println!("{}", doc.summary().unwrap().text(result.source()));
for section in doc.sections() {
if section.section_kind(result.source()) == GoogleSectionKind::Args {
for arg in section.args() {
println!("{}: {}",
arg.name().text(result.source()),
arg.r#type().map(|t| t.text(result.source())).unwrap_or(""));
}
}
}NumPy style works the same way — use parse_numpy / NumPyDocstring instead.
use pydocstring::{detect_style, Style};
assert_eq!(detect_style("Summary.\n\nArgs:\n x: Desc."), Style::Google);
assert_eq!(detect_style("Summary.\n\nParameters\n----------\nx : int"), Style::NumPy);Every token carries byte offsets for precise diagnostics:
use pydocstring::google::{parse_google, nodes::GoogleDocstring};
use pydocstring::GoogleSectionKind;
let result = parse_google("Summary.\n\nArgs:\n x (int): The value.");
let doc = GoogleDocstring::cast(result.root()).unwrap();
for section in doc.sections() {
if section.section_kind(result.source()) == GoogleSectionKind::Args {
for arg in section.args() {
let name = arg.name();
println!("'{}' at byte {}..{}",
name.text(result.source()), name.range().start(), name.range().end());
}
}
}The parse result is a tree of SyntaxNode (branches) and SyntaxToken (leaves), each tagged with a SyntaxKind. Use pretty_print() to visualize:
use pydocstring::google::parse_google;
let result = parse_google("Summary.\n\nArgs:\n x (int): The value.");
println!("{}", result.pretty_print());GOOGLE_DOCSTRING@0..42 {
SUMMARY: "Summary."@0..8
GOOGLE_SECTION@10..42 {
GOOGLE_SECTION_HEADER@10..15 {
NAME: "Args"@10..14
COLON: ":"@14..15
}
GOOGLE_ARG@20..42 {
NAME: "x"@20..21
OPEN_BRACKET: "("@22..23
TYPE: "int"@23..26
CLOSE_BRACKET: ")"@26..27
COLON: ":"@27..28
DESCRIPTION: "The value."@29..39
}
}
}
Walk the tree with the Visitor trait for style-agnostic analysis:
use pydocstring::{Visitor, walk, SyntaxToken, SyntaxKind};
use pydocstring::google::parse_google;
struct NameCollector<'a> {
source: &'a str,
names: Vec<String>,
}
impl Visitor for NameCollector<'_> {
fn visit_token(&mut self, token: &SyntaxToken) {
if token.kind() == SyntaxKind::NAME {
self.names.push(token.text(self.source).to_string());
}
}
}
let result = parse_google("Summary.\n\nArgs:\n x: Desc.\n y: Desc.");
let mut collector = NameCollector { source: result.source(), names: vec![] };
walk(result.root(), &mut collector);
assert_eq!(collector.names, vec!["Args", "x", "y"]);Both styles support the following section categories. Typed accessor methods are available on each style's section node.
| Category | NumPy | |
|---|---|---|
| Parameters | args() → GoogleArg |
parameters() → NumPyParameter |
| Returns / Yields | returns() → GoogleReturns |
returns() → NumPyReturns |
| Raises | exceptions() → GoogleException |
exceptions() → NumPyException |
| Warns | warnings() → GoogleWarning |
warnings() → NumPyWarning |
| See Also | see_also_items() → GoogleSeeAlsoItem |
see_also_items() → NumPySeeAlsoItem |
| Attributes | attributes() → GoogleAttribute |
attributes() → NumPyAttribute |
| Methods | methods() → GoogleMethod |
methods() → NumPyMethod |
| Free text (Notes, Examples, etc.) | body_text() |
body_text() |
Root-level accessors: summary(), extended_summary() (NumPy also has deprecation()).
cargo build
cargo test
cargo run --example parse_google
cargo run --example parse_numpy