Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: CI

on:
push:
branches: [main]
pull_request:
branches: [main]

env:
CARGO_TERM_COLOR: always

jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy

- name: Cache cargo
uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}

- name: Check formatting
run: cargo fmt --all -- --check

- name: Clippy
run: cargo clippy --all-targets -- -D warnings

- name: Build
run: cargo build --verbose

- name: Run tests
run: cargo test --verbose
58 changes: 58 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
name: Release

on:
push:
tags:
- "v*"

env:
CARGO_TERM_COLOR: always

jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Install Rust
uses: dtolnay/rust-toolchain@stable

- name: Cache cargo
uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}

- name: Run tests
run: cargo test --verbose

publish:
needs: test
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4

- name: Install Rust
uses: dtolnay/rust-toolchain@stable

- name: Verify version matches tag
run: |
CARGO_VERSION="v$(cargo metadata --no-deps --format-version 1 | python3 -c 'import sys,json; print(json.load(sys.stdin)["packages"][0]["version"])')"
TAG="${GITHUB_REF#refs/tags/}"
if [ "$CARGO_VERSION" != "$TAG" ]; then
echo "::error::Cargo.toml version ($CARGO_VERSION) does not match tag ($TAG)"
exit 1
fi

- name: Publish to crates.io
run: cargo publish --token ${{ secrets.CARGO_REGISTRY_TOKEN }}

- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
generate_release_notes: true
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "pydocstring"
version = "0.1.0"
version = "0.0.1"
edition = "2024"
authors = ["Ryuma Asai"]
description = "A fast, zero-dependency Rust parser for Python docstrings (NumPy and Google styles) with full AST and source location tracking"
Expand Down
25 changes: 20 additions & 5 deletions examples/parse_google.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,16 @@ Raises:
.collect();
println!("\nArgs ({}):", args.len());
for arg in &args {
let type_str = arg.r#type.as_ref().map(|t| t.source_text(&doc.source)).unwrap_or("?");
let type_str = arg
.r#type
.as_ref()
.map(|t| t.source_text(&doc.source))
.unwrap_or("?");
println!(
" {} ({}): {}",
arg.name.source_text(&doc.source), type_str, arg.description.source_text(&doc.source)
arg.name.source_text(&doc.source),
type_str,
arg.description.source_text(&doc.source)
);
}

Expand All @@ -63,7 +69,11 @@ Raises:
.as_ref()
.map(|t| t.source_text(&doc.source))
.unwrap_or("?");
println!("\nReturns: {}: {}", type_str, ret.description.source_text(&doc.source));
println!(
"\nReturns: {}: {}",
type_str,
ret.description.source_text(&doc.source)
);
}

let raises: Vec<_> = doc
Expand All @@ -80,7 +90,11 @@ Raises:
.collect();
println!("\nRaises ({}):", raises.len());
for exc in &raises {
println!(" {}: {}", exc.r#type.source_text(&doc.source), exc.description.source_text(&doc.source));
println!(
" {}: {}",
exc.r#type.source_text(&doc.source),
exc.description.source_text(&doc.source)
);
}

let all_sections: Vec<_> = doc
Expand All @@ -95,7 +109,8 @@ Raises:
for section in &all_sections {
println!(
" {} (header: {:?})",
section.header.name.source_text(&doc.source), section.header.range
section.header.name.source_text(&doc.source),
section.header.range
);
}
}
13 changes: 10 additions & 3 deletions examples/parse_numpy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,11 @@ Examples
NumPySectionBody::Parameters(params) => {
println!("\nParameters:");
for param in params {
let names: Vec<&str> =
param.names.iter().map(|n| n.source_text(&doc.source)).collect();
let names: Vec<&str> = param
.names
.iter()
.map(|n| n.source_text(&doc.source))
.collect();
println!(
" - {:?}: {:?}",
names,
Expand All @@ -80,7 +83,11 @@ Examples
NumPySectionBody::Raises(excs) => {
println!("\nRaises:");
for exc in excs {
println!(" - {}: {}", exc.r#type.source_text(&doc.source), exc.description.source_text(&doc.source));
println!(
" - {}: {}",
exc.r#type.source_text(&doc.source),
exc.description.source_text(&doc.source)
);
}
}
NumPySectionBody::Notes(text) => {
Expand Down
8 changes: 6 additions & 2 deletions examples/test_ret.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use pydocstring::google::parse_google;
use pydocstring::GoogleSectionBody;
use pydocstring::google::parse_google;

fn main() {
let input = "\
Expand All @@ -22,7 +22,11 @@ Returns:
for (idx, item) in doc.items.iter().enumerate() {
match item {
pydocstring::GoogleDocstringItem::Section(s) => {
println!("Item {}: Section {:?}", idx, s.header.name.source_text(&doc.source));
println!(
"Item {}: Section {:?}",
idx,
s.header.name.source_text(&doc.source)
);
if let GoogleSectionBody::Returns(ref ret) = s.body {
let type_str = ret.return_type.as_ref().map(|t| t.source_text(&doc.source));
let d = &ret.description.source_text(&doc.source);
Expand Down
6 changes: 5 additions & 1 deletion src/styles/google/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -458,6 +458,10 @@ impl Default for GoogleDocstring {

impl fmt::Display for GoogleDocstring {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "GoogleDocstring(summary: {})", self.summary.source_text(&self.source))
write!(
f,
"GoogleDocstring(summary: {})",
self.summary.source_text(&self.source)
)
}
}
20 changes: 10 additions & 10 deletions src/styles/numpy/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -600,12 +600,12 @@ fn parse_name_and_type(
let seg_abs =
type_abs_start + seg_offset + (seg_raw.len() - seg_raw.trim_start().len());
optional = Some(TextRange::from_offset_len(seg_abs, "optional".len()));
} else if seg.starts_with("default") {
} else if let Some(stripped) = seg.strip_prefix("default") {
let ws_lead = seg_raw.len() - seg_raw.trim_start().len();
let kw_abs = type_abs_start + seg_offset + ws_lead;
default_keyword = Some(TextRange::from_offset_len(kw_abs, "default".len()));

let after_kw = seg["default".len()..].trim_start();
let after_kw = stripped.trim_start();
if let Some(rest) = after_kw.strip_prefix('=') {
let sep_pos = seg.find('=').unwrap();
let sep_abs = kw_abs + sep_pos;
Expand Down Expand Up @@ -986,10 +986,10 @@ fn parse_references(
let end_col = current_col + content.lines().last().unwrap_or("").len();
refs.push(crate::styles::numpy::ast::NumPyReference {
range: cursor.make_range(start_l, current_col, end_l, end_col),
directive_marker: current_directive_marker.clone(),
open_bracket: current_open_bracket.clone(),
number: current_number.clone(),
close_bracket: current_close_bracket.clone(),
directive_marker: current_directive_marker,
open_bracket: current_open_bracket,
number: current_number,
close_bracket: current_close_bracket,
content: cursor.make_range(start_l, current_col, end_l, end_col),
});
}
Expand Down Expand Up @@ -1035,10 +1035,10 @@ fn parse_references(
let end_col = current_col + content.lines().last().unwrap_or("").len();
refs.push(crate::styles::numpy::ast::NumPyReference {
range: cursor.make_range(start_l, current_col, end_l, end_col),
directive_marker: current_directive_marker.clone(),
open_bracket: current_open_bracket.clone(),
number: current_number.clone(),
close_bracket: current_close_bracket.clone(),
directive_marker: current_directive_marker,
open_bracket: current_open_bracket,
number: current_number,
close_bracket: current_close_bracket,
content: cursor.make_range(start_l, current_col, end_l, end_col),
});
current_content_lines.clear();
Expand Down