Metadata-Version: 2.4
Name: confext
Version: 0.7.2
Summary: Stdlib HTML parsing into a tree model
Author-email: kinishuki <kinishuki@gmail.com>
License: MIT
Keywords: html,parser,tree
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
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: Programming Language :: Python :: 3.14
Classifier: Topic :: Text Processing :: Markup :: HTML
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Dynamic: license-file

## Quick start

```python
import confext

html = '''
<!DOCTYPE html>
<html><body>
  <div id="main" class="wrap card">
    <p class="lead">Hello <b>world</b></p>
    <ul><li>1</li><li>2</li></ul>
  </div>
</body></html>
'''

doc = confext.parse(html)              # -> parse html
doc.doctype                            # '<!DOCTYPE html>'
doc.stats()                            # {'elements': 7, 'comments': 0, 'max_depth': 4}
doc.to_html()                          # serialized markup
doc.root.iter_elements()               # walk every element node
```

## API

### Functions

| Function | Description |
|---|---|
| `parse(html)` | Parse `str` or `bytes` into a `ParsedDocument` using the default engine. |

That is the whole module-level API, confext is a parser, nothing else.

### ParsedDocument

| Member | Description |
|---|---|
| `to_html()` | Re-serialize the tree. |
| `stats()` | `{'elements', 'comments', 'max_depth'}`. |
| `to_dict()` | `{'doctype', 'stats', 'html', 'errors'}`. |
| `doctype`, `root`, `comments`, `errors` | Raw parse artifacts. |

### Node

`tag`, `attrs`, `parent`, `children` (mix of `Node`s and text `str`s),
plus: `get_attr`, `has_class`, `append_child`, `remove`, `text_content`,
`iter_elements`, `to_html`, `to_dict`.

### Engine

For isolated runs with a custom config and no global state:

```python
from confext import Engine, ConfextError

engine = Engine({'strict_mode': True})
doc = engine.parse('<div><p>ok</p></div>')
engine.parse('<div><p>oops</div>')   # raises ConfextError in strict mode
```

Also accepts `use_cache=False` to disable document memoization
(`cache_size` is clamped to a minimum of 1).

## Configuration

`DEFAULT_CONFIG` is a plain dict; `Engine(config)` merges over it.
Settings can also be persisted to and loaded from a `default.config`
file:

```python
from confext import Config, load_config, save_config

cfg = load_config()                    # finds the caller's default.config
cfg.get('max_depth')                   # user setting layered over defaults
save_config(cfg, 'default.config', compression='xz')
```

The binary layout is `[8-byte sha256][4-byte size][payload]`, where
the payload is a raw, lzma (xz), or zlib compressed payload of a
plain-data dict. Size and checksum are verified before decompression
and unpickling; both checks run before any decompression is
attempted. Failures raise `ConfigLoadError`, `ConfigChecksumError`,
or `ConfigDecompressError`.

| Key | Default | Used by |
|---|---|---|
| `encoding` | `'utf-8'` | Fallback when bytes input has no BOM / `<meta charset>`. |
| `max_depth` | `100` | Element nesting cap (deeper nodes are dropped). |
| `max_file_size_mb` | `10` | Input size cap; parse raises `ConfextError` beyond it. |
| `cache_size` | `50` | LRU capacity for parsed documents. |
| `strict_mode` | `False` | If true, malformed HTML (unclosed/stray tags) raises. |
| `strip_whitespace` | `True` | Collapse whitespace runs in text nodes at parse time. |
| `preserve_comments` | `False` | Keep comments in the tree (always captured in `doc.comments`). |

Output and sanitize keys (`remove_comments`, `pretty_indent`,
`allowed_tags`, …) are consumed by paster, not confext.

## Errors

```
ConfextError  parse failures, strict-mode violations (unclosed/
                  stray tags and depth-limit breaches — deeper nodes
                  are silently dropped outside strict mode) and
                  size-limit breaches
```

Invalid selectors now raise paster's `SelectorSyntaxError`.

## Layout

```
confext/
├── __init__.py     public API: parse; re-exports Engine,
│                   DEFAULT_CONFIG, ConfextError, Node,
│                   ParsedDocument, VOID_ELEMENTS, Config,
│                   load_config/save_config, config errors
├── errors.py       ConfextError, ConfigLoadError family
├── tree.py         Node, ParsedDocument (tree model + stats)
├── reader.py       encoding detection, TreeBuilder (stdlib tokenizer)
├── engine.py       Engine, DEFAULT_CONFIG, LRU cache
└── config.py       Config, load_config/save_config (default.config)
```
