Metadata-Version: 2.4
Name: pydocstring-rs
Version: 0.1.1
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Rust
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
Classifier: Topic :: Software Development :: Documentation
Classifier: Topic :: Text Processing :: Markup
License-File: LICENSE
Summary: Python bindings for pydocstring — a zero-dependency Rust parser for Python docstrings (Google and NumPy styles) with a unified syntax tree and byte-precise source locations
Keywords: docstring,parser,google,numpy,documentation
Author: Ryuma Asai
License: MIT
Requires-Python: >=3.10
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
Project-URL: Documentation, https://github.com/qraqras/pydocstring
Project-URL: Repository, https://github.com/qraqras/pydocstring

# pydocstring-rs

[![PyPI - Version](https://img.shields.io/pypi/v/pydocstring-rs?color=0062A8)](https://pypi.org/project/pydocstring-rs/)
[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/pydocstring-rs?color=0062A8)](https://devguide.python.org/versions/)
[![Crates.io Version](https://img.shields.io/crates/v/pydocstring?color=FFC12d)](https://crates.io/crates/pydocstring)
[![Crates.io MSRV](https://img.shields.io/crates/msrv/pydocstring?color=FFC12d)](https://blog.rust-lang.org/2025/02/20/Rust-1.85.0)

Python bindings for [pydocstring](https://crates.io/crates/pydocstring) — a zero-dependency Rust parser for Python docstrings (Google and NumPy styles).

Produces a **unified syntax tree** with **byte-precise source locations** on every token — designed as infrastructure for linters and formatters.

## Features

- **Full syntax tree** — builds a complete AST, not just extracted fields; traverse it with `walk()`
- **Typed objects per style** — style-specific classes like `GoogleArg`, `NumPyParameter`
- **Byte-precise source locations** — every token carries its exact byte range for pinpoint diagnostics
- **Powered by Rust** — native extension with no Python runtime overhead
- **Error-resilient** — never raises exceptions; malformed input still yields a best-effort tree
- **Style auto-detection** — hand it a docstring, get back `Style.GOOGLE` or `Style.NUMPY`

## Installation

```bash
pip install pydocstring-rs
```

## Usage

### Style Detection

```python
from pydocstring import detect_style, Style

detect_style("Summary.\n\nArgs:\n    x: Desc.")       # Style.GOOGLE
detect_style("Summary.\n\nParameters\n----------\n")  # Style.NUMPY
```

### Google Style

```python
from pydocstring import parse_google

doc = parse_google("""Summary line.

Args:
    x (int): The first value.
    y (str): The second value.

Returns:
    bool: True if successful.

Raises:
    ValueError: If x is negative.
""")

# Summary
print(doc.summary.text)  # "Summary line."

# Sections
for section in doc.sections:
    print(section.kind)  # "Args", "Returns", "Raises"

    for arg in section.args:
        print(f"  {arg.name.text}: {arg.type.text} — {arg.description.text}")

    if section.returns:
        r = section.returns
        print(f"  -> {r.return_type.text}: {r.description.text}")

    for exc in section.exceptions:
        print(f"  raises {exc.type.text}: {exc.description.text}")
```

### NumPy Style

```python
from pydocstring import parse_numpy

doc = parse_numpy("""Summary line.

Parameters
----------
x : int
    The first value.
y : str
    The second value.

Returns
-------
bool
    True if successful.
""")

print(doc.summary.text)  # "Summary line."

for section in doc.sections:
    print(section.kind)  # "Parameters", "Returns"

    for param in section.parameters:
        names = [n.text for n in param.names]
        print(f"  {names}: {param.type.text} — {param.description.text}")

    for ret in section.returns:
        print(f"  -> {ret.return_type.text}: {ret.description.text}")
```

### AST Access

Every parsed result exposes the full syntax tree via the `node` property:

```python
doc = parse_google("Summary.\n\nArgs:\n    x (int): Value.")

# Raw tree node
print(doc.node.kind)      # "GOOGLE_DOCSTRING"
print(doc.node.children)  # list of Node and Token objects

# Pretty-printed tree
print(doc.pretty_print())
```

Output:

```text
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: "Value."@29..35
    }
  }
}
```

### Tree Traversal

Use `walk()` for depth-first traversal of the syntax tree:

```python
from pydocstring import parse_google, walk, Token

doc = parse_google("Summary.\n\nArgs:\n    x (int): Value.")

for item in walk(doc.node):
    if isinstance(item, Token) and item.kind == "NAME":
        print(item.text)  # "Args", "x"
```

### Source Locations

All tokens carry byte-precise source ranges:

```python
doc = parse_google("Summary.\n\nArgs:\n    x (int): Value.")
token = doc.summary
print(token.range.start, token.range.end)  # 0 8
```

### Style-Independent Model (IR)

Convert any parsed docstring into a style-independent intermediate representation for analysis or transformation:

```python
from pydocstring import parse_google

parsed = parse_google("Summary.\n\nArgs:\n    x (int): The value.\n")
doc = parsed.to_model()

print(doc.summary)  # "Summary."

for section in doc.sections:
    if section.kind == "Parameters":
        for param in section.parameters:
            print(param.names)            # ["x"]
            print(param.type_annotation)  # "int"
            print(param.description)      # "The value."
```

### Emitting (Code Generation)

Re-emit a `Docstring` model in any style — useful for style conversion or formatting:

```python
from pydocstring import Docstring, Section, Parameter, emit_google, emit_numpy

doc = Docstring(
    summary="Brief summary.",
    sections=[
        Section(
            "Parameters",
            parameters=[
                Parameter(
                    ["x"],
                    type_annotation="int",
                    description="The value.",
                ),
            ],
        ),
    ],
)

google = emit_google(doc)
print(google)  # Contains "Args:"

numpy = emit_numpy(doc)
print(numpy)  # Contains "Parameters\n----------"
```

Combine parsing and emitting to convert between styles:

```python
from pydocstring import parse_google, emit_numpy

parsed = parse_google("Summary.\n\nArgs:\n    x (int): The value.\n")
doc = parsed.to_model()
numpy_text = emit_numpy(doc)
print(numpy_text)  # Contains "Parameters\n----------"
```

## API Reference

### Functions

| Function             | Returns           | Description                                    |
|----------------------|-------------------|------------------------------------------------|
| `parse_google(text)` | `GoogleDocstring` | Parse a Google-style docstring                 |
| `parse_numpy(text)`  | `NumPyDocstring`  | Parse a NumPy-style docstring                  |
| `detect_style(text)` | `Style`           | Detect style: `Style.GOOGLE` or `Style.NUMPY`  |
| `emit_google(doc)`   | `str`             | Emit a `Docstring` model as Google-style text  |
| `emit_numpy(doc)`    | `str`             | Emit a `Docstring` model as NumPy-style text   |

### Objects

| Class             | Key Properties                                                                                                   |
|-------------------|------------------------------------------------------------------------------------------------------------------|
| `Style`           | `GOOGLE`, `NUMPY` (enum)                                                                                         |
| `GoogleDocstring` | `summary`, `extended_summary`, `sections`, `node`, `source`, `pretty_print()`, `to_model()`                      |
| `GoogleSection`   | `kind`, `args`, `returns`, `exceptions`, `body_text`, `node`                                                     |
| `GoogleArg`       | `name`, `type`, `description`, `optional`                                                                        |
| `GoogleReturns`   | `return_type`, `description`                                                                                     |
| `GoogleException` | `type`, `description`                                                                                            |
| `NumPyDocstring`  | `summary`, `extended_summary`, `sections`, `node`, `source`, `pretty_print()`, `to_model()`                      |
| `NumPySection`    | `kind`, `parameters`, `returns`, `exceptions`, `body_text`, `node`                                               |
| `NumPyParameter`  | `names`, `type`, `description`, `optional`, `default_value`                                                      |
| `NumPyReturns`    | `name`, `return_type`, `description`                                                                             |
| `NumPyException`  | `type`, `description`                                                                                            |
| `Token`           | `kind`, `text`, `range`                                                                                          |
| `Node`            | `kind`, `range`, `children`                                                                                      |
| `TextRange`       | `start`, `end`                                                                                                   |
| `Docstring`       | `summary`, `extended_summary`, `deprecation`, `sections`                                                         |
| `Section` (model) | `kind`, `parameters`, `returns`, `exceptions`, `attributes`, `methods`, `see_also_entries`, `references`, `body` |
| `Parameter`       | `names`, `type_annotation`, `description`, `is_optional`, `default_value`                                        |
| `Return`          | `name`, `type_annotation`, `description`                                                                         |
| `ExceptionEntry`  | `type_name`, `description`                                                                                       |
| `Attribute`       | `name`, `type_annotation`, `description`                                                                         |
| `Method`          | `name`, `type_annotation`, `description`                                                                         |
| `SeeAlsoEntry`    | `names`, `description`                                                                                           |
| `Reference`       | `number`, `content`                                                                                              |
| `Deprecation`     | `version`, `description`                                                                                         |

## Development

### Prerequisites

- Rust (stable)
- Python 3.10+
- maturin

### Build

```bash
cd bindings/python

# Create virtual environment
python3 -m venv .venv
source .venv/bin/activate

# Install maturin
pip install maturin

# Build and install in development mode
maturin develop

# Verify
python -c "import pydocstring; print(pydocstring.detect_style('Args:\n    x: y'))"
```

### Build a wheel

```bash
maturin build --release
# Output: target/wheels/pydocstring-*.whl
```

### Publish to PyPI

```bash
maturin publish
```

