Metadata-Version: 2.4
Name: eazydraw
Version: 1.0.0
Summary: Python client and MCP server for the EazyDraw Automation API
Author-email: Dave Mattson <davemattson@eazydraw.com>
Maintainer-email: "Dekorra Optics, LLC" <davemattson@eazydraw.com>
License: MIT License
        
        Copyright (c) 2026 Dekorra Optics, LLC
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
        
Project-URL: Homepage, https://www.eazydraw.com
Project-URL: Documentation, https://www.eazydraw.com/ezdHelpPages/apiReference.htm
Keywords: eazydraw,automation,api,rest,mcp,model context protocol,claude,drawing,vector,graphics
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: MacOS
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Topic :: Multimedia :: Graphics
Classifier: Topic :: Software Development :: Libraries
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: requests>=2.31
Requires-Dist: pydantic>=2.6
Provides-Extra: mcp
Requires-Dist: mcp>=1.2; extra == "mcp"
Provides-Extra: dev
Requires-Dist: pyyaml>=6.0; extra == "dev"
Requires-Dist: openapi-spec-validator>=0.7; extra == "dev"
Dynamic: license-file

# EazyDraw Automation API — Python client

Python wrapper around the HTTP API documented in `../../API.md` and
`../../spec/openapi.yaml`. A developer tool, not (yet) a shipped SDK.

Packaged as the `eazydraw` module:

```
eazydraw/
  __init__.py    public exports (EazyDraw, EazyDrawError, models)
  client.py      the EazyDraw HTTP client (returns raw dicts)
  models.py      pydantic v2 models mirroring spec/openapi.yaml
eazydraw_api.py  backward-compat shim (re-exports from eazydraw)
```

## Setup

```bash
pip install -r requirements.txt        # requests + pydantic (+ dev: pyyaml, openapi-spec-validator)
# or, to install the package itself (editable):  pip install -e .
cp config.example.py config.py
# edit config.py and paste your bearer token from API Settings -> Reveal
```

Python 3.10+. `config.py` is gitignored so your real token stays on your
machine; `config.example.py` is the committed placeholder.

## Quickstart

```python
from eazydraw import EazyDraw          # (the old `from eazydraw_api import EazyDraw` still works)

# Paste your bearer token from API Settings -> Reveal
ed = EazyDraw(token="7b3f...")

ed.status()
# {'status': 'OK', 'version': '12.3.6', 'build': '51059'}

# Open a drawing, capture its uuid
d = ed.open_drawing("~/Documents/sketch.ezdjson")
print(d["uuid"], "newly opened" if ed.last_response.status_code == 201 else "already open")

# Walk down to graphics on the active layer
layers = ed.layers(d["uuid"])
gs = ed.layer_graphics(d["uuid"], layers[0]["uuid"])
print(len(gs), "graphics on layer 0")

# Place a library element into the drawing
libs = ed.libraries()
math_lib = next(l for l in libs if l["DisplayName"] == "Math")
els = ed.library_elements(math_lib["uuid"])
graphic_el = next(e for e in els if e["elementType"] == "graphic")
placed = ed.use_library_element(
    d["uuid"], layers[0]["uuid"], math_lib["uuid"], graphic_el["uuid"]
)
print("Placed", placed["class"], "as", placed["graphicUUID"])

# Export the drawing as a PNG to disk
ed.export_drawing(d["uuid"], "png", save_to="/tmp/sketch.png")

# Close it
ed.close_drawing(d["uuid"])
```

## Typed models (optional)

The client returns raw dicts. For validation / IDE support, parse them with the
pydantic models in `eazydraw.models` (these mirror `spec/openapi.yaml`):

```python
from eazydraw import EazyDraw, Graphic, Text

ed = EazyDraw(token="7b3f...")
g = Graphic.model_validate(ed.graphic(D, L, G))
print(g.graphic_uuid, g.hidden_bounds.width, g.is_group)   # snake_case fields

# round-trips back to wire form (camelCase / PascalCase keys)
g.model_dump(by_alias=True)
```

Field names are snake_case with the wire keys as aliases; unknown server keys
are preserved (`extra="allow"`), so a newer server field won't break parsing.

## Error handling

Non-2xx responses raise `EazyDrawError(status_code, message, body)`:

```python
from eazydraw import EazyDraw, EazyDrawError

ed = EazyDraw(token="wrong-token")
try:
    ed.status()
except EazyDrawError as exc:
    print(exc.status_code, exc.message)  # 401 "Missing or invalid bearer token"
```

## Status codes

Every method calls `self.last_response = resp`, so when the server's status
code carries semantic meaning (201 vs 200 on POST, etc.), inspect it
afterward:

```python
d = ed.open_drawing(path)
already_open = (ed.last_response.status_code == 200)
```

## Conventions

- Methods take UUIDs as positional strings, in path order: `ed.layer(D, L)`,
  `ed.layer_graphics(D, L)`, etc.
- Recursive group-traversal methods take a variadic list of UUIDs in chain
  order: `ed.group_graphics(D, L, G1, G2, G3)` reads as "drilling down".
- `export_*` methods take `fmt` as a keyword argument (so the call site stays
  readable when there's a long UUID chain) and accept an optional `save_to`
  path; if given, bytes are written to that path and the path is returned.
  Otherwise raw bytes are returned.
- Collection endpoints unwrap the envelope: `ed.drawings()` returns the list
  directly, not `{"drawings": [...]}`.
- `client.py` is single-class; as we add API endpoints we add a method — no
  inheritance, no abstractions. `models.py` is plain pydantic data classes.

## Not shipped

This lives in the repo for convenience but is not part of the EazyDraw app
bundle. A versioned, published Python SDK is a future product decision; the
`eazydraw` package here is the working basis for it (and for the semantic
resolver + MCP server layers to come).
