Metadata-Version: 2.4
Name: xyang
Version: 0.1.0
Summary: A Python library implementing a subset of YANG features focused on constraint validation
Home-page: https://github.com/exergy-connect/xYang
Author: Exergy LLC
License-Expression: MIT
Project-URL: Homepage, https://github.com/exergy-connect/xYang
Project-URL: Repository, https://github.com/exergy-connect/xYang.git
Project-URL: Issues, https://github.com/exergy-connect/xYang/issues
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
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: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: System :: Networking
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: dev
Requires-Dist: pytest>=9.0.3; extra == "dev"
Requires-Dist: black>=26.3.1; extra == "dev"
Requires-Dist: PyYAML>=6.0.3; extra == "dev"
Dynamic: home-page
Dynamic: license-file
Dynamic: requires-python

# xYang

**YANG models, validated JSON, and JSON Schema—without dragging in a full management stack.**

xYang is a pure-Python library and CLI for **parsing YANG 1.1-shaped modules**, **validating instance data** against the full set of **RFC 7950 built-in types** (each checked with the right rules—ranges, `length`/`pattern`, base64 **`binary`**, **`bits`**, **`union`** disambiguation, **`leafref`** / **`identityref`** / **`instance-identifier`** resolution, and the rest), plus **`must`**, **`when`**, **`if-feature`**, and structure. It **exports** a standards-friendly **JSON Schema (2020-12)** layer augmented with **`x-yang`** metadata for round-trip where supported. It is built for real modules (reference design: [`examples/meta-model.yang`](examples/meta-model.yang)), including **deep** XPath: **`deref()`** tied to schema paths, **`union`** typing, and **`current()`** in list and leaf-list contexts.

- **Zero required runtime dependencies** — drop into apps, agents, and pipelines with minimal footprint.
- **Honest scope** — not every RFC 7950 statement is modeled; what *is* implemented is described precisely in [**FEATURES.md**](FEATURES.md) (including `import` / `include`, `if-feature`, `anydata` / `anyxml`, and JSON `if-features` round-trip). Constructs such as `rpc`, `notification`, and `deviation` are **recognized and skipped** with a log warning so mixed modules still parse.
- **MIT licensed** — use it in products and internal tools alike.

**Repository:** [github.com/exergy-connect/xYang](https://github.com/exergy-connect/xYang) · **Issues:** [github.com/exergy-connect/xYang/issues](https://github.com/exergy-connect/xYang/issues)

---

## Features (overview)

The list below is the short version; [**FEATURES.md**](FEATURES.md) is the authoritative, line-by-line feature matrix and documents the **YANG.json** hybrid format.

- **Module structure**: `module` / `submodule`, `yang-version`, `namespace`, `prefix`, metadata, `revision`, `import`, `include`, `feature`
- **Types**: **All RFC 7950 built-in types** (Section 4.2.4) are **supported and validated** on instance data—`string`, `binary`, numeric types, `decimal64`, `boolean`, `empty`, `enumeration`, `bits`, `union`, `leafref`, `identityref`, and `instance-identifier`—with applicable substatements (`length`, `range`, `pattern`, `fraction-digits`, `require-instance`, etc.). **`typedef`** and imports compose these. Statement-level scope (e.g. skipped `rpc` / `notification`) and JSON Schema details: **FEATURES.md**.
- **Data nodes**: `container`, `list` + `key`, `leaf`, `leaf-list`, `choice` / `case`, `anydata` / `anyxml`, `grouping` / `uses` / `refine`, `augment` (merge when uses expansion is enabled)
- **Constraints**: `must`, `when`, `if-feature`, `mandatory`, `default`, `min-elements` / `max-elements`, `pattern`, `length`, `range`, `fraction-digits`
- **References**: `leafref` (+ `require-instance`), `instance-identifier`, `identityref`, `identity` / `base`, XPath `derived-from()` / `derived-from-or-self()`
- **Interop**: **`xyang`** CLI (`parse`, `validate`, `convert`) and **JSON Schema** export with **`x-yang`** annotations for generator/parser round-trip where supported

---

## Installation

**From PyPI** (when published):

```bash
pip install xyang
```

**From a checkout** (editable, for development):

```bash
pip install -e .
```

There are **no required runtime dependencies**. For **`xyang validate`** with **`.yaml` / `.yml`** instance files, install **PyYAML** (`pip install PyYAML` or `pip install -e ".[dev]"`).

**Requirements:** Python **≥ 3.9** (see `pyproject.toml`).

---

## Usage

### Command-line (`xyang`)

```bash
xyang -h                    # help
xyang parse <file.yang>     # print module info
xyang validate <file.yang> [data.json]  # or .yaml/.yml (needs PyYAML); omit file → JSON from stdin
xyang convert <file.yang> [-o path]     # YANG → .yang.json (output path ends with .yang.json)
```

Without installing the package, from the repo root: `PYTHONPATH=src python3 -m xyang -h`

### Parsing a YANG module

```python
from xyang import parse_yang_file, parse_yang_string

# Parse from file
module = parse_yang_file("examples/meta-model.yang")

# Parse from string
yang_content = """
module example {
  yang-version 1.1;
  namespace "urn:example";
  prefix "ex";
  
  container data {
    leaf name {
      type string;
    }
  }
}
"""
module = parse_yang_string(yang_content)

print(f"Module: {module.name}")
print(f"Namespace: {module.namespace}")
print(f"Prefix: {module.prefix}")
```

### Validating data

```python
from xyang import parse_yang_file, YangValidator

module = parse_yang_file("examples/meta-model.yang")
validator = YangValidator(module)

# Consolidated JSON document: one tree matching your module’s data layout.
# XPath comparisons use schema-aware coercion (e.g. string "true" vs boolean leaves).
data = {
    "data-model": {
        "name": "example",
        "entities": [
            {
                "name": "server",
                "fields": [
                    {"name": "id", "type": "string"}
                ]
            }
        ]
    }
}

is_valid, errors, warnings = validator.validate(data)
if not is_valid:
    for error in errors:
        print(f"Error: {error}")
```

### Working with types

```python
from xyang import TypeConstraint, TypeSystem

type_system = TypeSystem()
constraint = TypeConstraint(
    pattern=r'[a-z_][a-z0-9_]*',
    length="1..64"
)
type_system.register_typedef("entity-name", "string", constraint)

is_valid, error = type_system.validate("server_name", "entity-name")
print(f"Valid: {is_valid}")
```

### Converting YANG → JSON Schema (`.yang.json`)

Valid **JSON Schema** for structure and types; YANG-only rules (`must`, `when`, leafref paths, `if-features`, …) ride in **`x-yang`**. Details: [FEATURES.md — YANG.json hybrid format](FEATURES.md#yangjson-hybrid-format).

```python
from xyang.parser import YangParser
from xyang.json import schema_to_yang_json

parser = YangParser(expand_uses=False)
module = parser.parse_file("examples/meta-model.yang")
schema_to_yang_json(module, output_path="meta-model.yang.json")
```

CLI: `xyang convert examples/meta-model.yang -o meta-model.yang.json`

---

## Project layout

```
xYang/
├── src/xyang/
│   ├── __init__.py      # Package exports
│   ├── __main__.py      # CLI (parse, validate, convert)
│   ├── parser/          # YANG parser (incl. unsupported-statement skip)
│   ├── json/            # JSON Schema generator + parser
│   ├── validator/       # Document validation
│   ├── xpath/           # XPath for must/when
│   ├── ast.py           # AST nodes
│   ├── types.py         # Type system
│   ├── module.py        # Module model
│   └── errors.py
├── examples/            # meta-model.yang, samples, generated .yang.json
├── tests/
├── benchmarks/
├── FEATURES.md          # Full feature list & format spec
├── pyproject.toml
└── README.md
```

---

## XPath (schema-aware)

Coverage matches what **meta-model.yang** needs, evaluated with **schema context** (not a generic XPath 1.0 engine):

- **Paths**: `../field`, `../../field`, absolute paths such as `/data-model/entities`
- **Functions**: `string()`, `number()`, `concat()`, `string-length()`, `translate()`, `count()`, **`deref()`**, **`current()`**, `not()`, `true()`, `false()`, `boolean()`, `derived-from()`, `derived-from-or-self()`, …
- **Comparisons & logic**: `=`, `!=`, `<=`, `>=`, `<`, `>`, `and`, `or`
- **Literal sequences** (xYang extension): RHS `('a', 'b')` for membership-style equality
- **Predicates & indexing**: e.g. `[name = current()]`, `[1]`
- **String concat**: `+` between strings in expressions

**`deref()`** on leafref values follows the **leafref’s schema path** to resolve the target node; it supports nesting, caching, and cycle detection for the patterns used in production modules here.

---

## When & must (examples)

**When** — if the condition is false, the node is out of the effective schema; data there is reported as invalid:

```yang
container item_type {
  when "../type = 'array'";
  leaf primitive { type string; }
}
```

**Must** — XPath must evaluate true or validation fails (with `error-message` when provided):

```yang
leaf minDate {
  type date;
  must "not(../maxDate) or . <= ../maxDate" {
    error-message "minDate must be less than or equal to maxDate";
  }
}
```

---

## Scope & limitations

- **Single JSON instance** — validation is against one consolidated document, not NETCONF/XML fragments or incremental edits.
- **XPath subset** — unsupported expressions fail at XPath parse time (`UnsupportedXPathError`). Extend the evaluator to add features.
- **`deref()`** — fully handled for meta-model-style patterns; it remains **schema-coupled** by design, not a standalone generic resolver.
- **RFC surface** — see [**FEATURES.md**](FEATURES.md) for what is partial, skipped, or out of scope; the parser warns when it skips unsupported top-level-like statements.

## Design choices

**No mandatory third-party stack** — core package dependencies are empty in `pyproject.toml`; optional **PyYAML** only for YAML instances on the CLI. That keeps xYang easy to embed and audit.

---

## Development

```bash
pip install -e ".[dev]"
pytest
black src/xyang/
```

---

## License

MIT License
