eazydraw 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
eazydraw-1.0.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Dekorra Optics, LLC
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,180 @@
1
+ Metadata-Version: 2.4
2
+ Name: eazydraw
3
+ Version: 1.0.0
4
+ Summary: Python client and MCP server for the EazyDraw Automation API
5
+ Author-email: Dave Mattson <davemattson@eazydraw.com>
6
+ Maintainer-email: "Dekorra Optics, LLC" <davemattson@eazydraw.com>
7
+ License: MIT License
8
+
9
+ Copyright (c) 2026 Dekorra Optics, LLC
10
+
11
+ Permission is hereby granted, free of charge, to any person obtaining a copy
12
+ of this software and associated documentation files (the "Software"), to deal
13
+ in the Software without restriction, including without limitation the rights
14
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15
+ copies of the Software, and to permit persons to whom the Software is
16
+ furnished to do so, subject to the following conditions:
17
+
18
+ The above copyright notice and this permission notice shall be included in all
19
+ copies or substantial portions of the Software.
20
+
21
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27
+ SOFTWARE.
28
+
29
+ Project-URL: Homepage, https://www.eazydraw.com
30
+ Project-URL: Documentation, https://www.eazydraw.com/ezdHelpPages/apiReference.htm
31
+ Keywords: eazydraw,automation,api,rest,mcp,model context protocol,claude,drawing,vector,graphics
32
+ Classifier: Development Status :: 4 - Beta
33
+ Classifier: Intended Audience :: Developers
34
+ Classifier: License :: OSI Approved :: MIT License
35
+ Classifier: Operating System :: MacOS
36
+ Classifier: Programming Language :: Python :: 3
37
+ Classifier: Programming Language :: Python :: 3.10
38
+ Classifier: Topic :: Multimedia :: Graphics
39
+ Classifier: Topic :: Software Development :: Libraries
40
+ Requires-Python: >=3.10
41
+ Description-Content-Type: text/markdown
42
+ License-File: LICENSE
43
+ Requires-Dist: requests>=2.31
44
+ Requires-Dist: pydantic>=2.6
45
+ Provides-Extra: mcp
46
+ Requires-Dist: mcp>=1.2; extra == "mcp"
47
+ Provides-Extra: dev
48
+ Requires-Dist: pyyaml>=6.0; extra == "dev"
49
+ Requires-Dist: openapi-spec-validator>=0.7; extra == "dev"
50
+ Dynamic: license-file
51
+
52
+ # EazyDraw Automation API — Python client
53
+
54
+ Python wrapper around the HTTP API documented in `../../API.md` and
55
+ `../../spec/openapi.yaml`. A developer tool, not (yet) a shipped SDK.
56
+
57
+ Packaged as the `eazydraw` module:
58
+
59
+ ```
60
+ eazydraw/
61
+ __init__.py public exports (EazyDraw, EazyDrawError, models)
62
+ client.py the EazyDraw HTTP client (returns raw dicts)
63
+ models.py pydantic v2 models mirroring spec/openapi.yaml
64
+ eazydraw_api.py backward-compat shim (re-exports from eazydraw)
65
+ ```
66
+
67
+ ## Setup
68
+
69
+ ```bash
70
+ pip install -r requirements.txt # requests + pydantic (+ dev: pyyaml, openapi-spec-validator)
71
+ # or, to install the package itself (editable): pip install -e .
72
+ cp config.example.py config.py
73
+ # edit config.py and paste your bearer token from API Settings -> Reveal
74
+ ```
75
+
76
+ Python 3.10+. `config.py` is gitignored so your real token stays on your
77
+ machine; `config.example.py` is the committed placeholder.
78
+
79
+ ## Quickstart
80
+
81
+ ```python
82
+ from eazydraw import EazyDraw # (the old `from eazydraw_api import EazyDraw` still works)
83
+
84
+ # Paste your bearer token from API Settings -> Reveal
85
+ ed = EazyDraw(token="7b3f...")
86
+
87
+ ed.status()
88
+ # {'status': 'OK', 'version': '12.3.6', 'build': '51059'}
89
+
90
+ # Open a drawing, capture its uuid
91
+ d = ed.open_drawing("~/Documents/sketch.ezdjson")
92
+ print(d["uuid"], "newly opened" if ed.last_response.status_code == 201 else "already open")
93
+
94
+ # Walk down to graphics on the active layer
95
+ layers = ed.layers(d["uuid"])
96
+ gs = ed.layer_graphics(d["uuid"], layers[0]["uuid"])
97
+ print(len(gs), "graphics on layer 0")
98
+
99
+ # Place a library element into the drawing
100
+ libs = ed.libraries()
101
+ math_lib = next(l for l in libs if l["DisplayName"] == "Math")
102
+ els = ed.library_elements(math_lib["uuid"])
103
+ graphic_el = next(e for e in els if e["elementType"] == "graphic")
104
+ placed = ed.use_library_element(
105
+ d["uuid"], layers[0]["uuid"], math_lib["uuid"], graphic_el["uuid"]
106
+ )
107
+ print("Placed", placed["class"], "as", placed["graphicUUID"])
108
+
109
+ # Export the drawing as a PNG to disk
110
+ ed.export_drawing(d["uuid"], "png", save_to="/tmp/sketch.png")
111
+
112
+ # Close it
113
+ ed.close_drawing(d["uuid"])
114
+ ```
115
+
116
+ ## Typed models (optional)
117
+
118
+ The client returns raw dicts. For validation / IDE support, parse them with the
119
+ pydantic models in `eazydraw.models` (these mirror `spec/openapi.yaml`):
120
+
121
+ ```python
122
+ from eazydraw import EazyDraw, Graphic, Text
123
+
124
+ ed = EazyDraw(token="7b3f...")
125
+ g = Graphic.model_validate(ed.graphic(D, L, G))
126
+ print(g.graphic_uuid, g.hidden_bounds.width, g.is_group) # snake_case fields
127
+
128
+ # round-trips back to wire form (camelCase / PascalCase keys)
129
+ g.model_dump(by_alias=True)
130
+ ```
131
+
132
+ Field names are snake_case with the wire keys as aliases; unknown server keys
133
+ are preserved (`extra="allow"`), so a newer server field won't break parsing.
134
+
135
+ ## Error handling
136
+
137
+ Non-2xx responses raise `EazyDrawError(status_code, message, body)`:
138
+
139
+ ```python
140
+ from eazydraw import EazyDraw, EazyDrawError
141
+
142
+ ed = EazyDraw(token="wrong-token")
143
+ try:
144
+ ed.status()
145
+ except EazyDrawError as exc:
146
+ print(exc.status_code, exc.message) # 401 "Missing or invalid bearer token"
147
+ ```
148
+
149
+ ## Status codes
150
+
151
+ Every method calls `self.last_response = resp`, so when the server's status
152
+ code carries semantic meaning (201 vs 200 on POST, etc.), inspect it
153
+ afterward:
154
+
155
+ ```python
156
+ d = ed.open_drawing(path)
157
+ already_open = (ed.last_response.status_code == 200)
158
+ ```
159
+
160
+ ## Conventions
161
+
162
+ - Methods take UUIDs as positional strings, in path order: `ed.layer(D, L)`,
163
+ `ed.layer_graphics(D, L)`, etc.
164
+ - Recursive group-traversal methods take a variadic list of UUIDs in chain
165
+ order: `ed.group_graphics(D, L, G1, G2, G3)` reads as "drilling down".
166
+ - `export_*` methods take `fmt` as a keyword argument (so the call site stays
167
+ readable when there's a long UUID chain) and accept an optional `save_to`
168
+ path; if given, bytes are written to that path and the path is returned.
169
+ Otherwise raw bytes are returned.
170
+ - Collection endpoints unwrap the envelope: `ed.drawings()` returns the list
171
+ directly, not `{"drawings": [...]}`.
172
+ - `client.py` is single-class; as we add API endpoints we add a method — no
173
+ inheritance, no abstractions. `models.py` is plain pydantic data classes.
174
+
175
+ ## Not shipped
176
+
177
+ This lives in the repo for convenience but is not part of the EazyDraw app
178
+ bundle. A versioned, published Python SDK is a future product decision; the
179
+ `eazydraw` package here is the working basis for it (and for the semantic
180
+ resolver + MCP server layers to come).
@@ -0,0 +1,129 @@
1
+ # EazyDraw Automation API — Python client
2
+
3
+ Python wrapper around the HTTP API documented in `../../API.md` and
4
+ `../../spec/openapi.yaml`. A developer tool, not (yet) a shipped SDK.
5
+
6
+ Packaged as the `eazydraw` module:
7
+
8
+ ```
9
+ eazydraw/
10
+ __init__.py public exports (EazyDraw, EazyDrawError, models)
11
+ client.py the EazyDraw HTTP client (returns raw dicts)
12
+ models.py pydantic v2 models mirroring spec/openapi.yaml
13
+ eazydraw_api.py backward-compat shim (re-exports from eazydraw)
14
+ ```
15
+
16
+ ## Setup
17
+
18
+ ```bash
19
+ pip install -r requirements.txt # requests + pydantic (+ dev: pyyaml, openapi-spec-validator)
20
+ # or, to install the package itself (editable): pip install -e .
21
+ cp config.example.py config.py
22
+ # edit config.py and paste your bearer token from API Settings -> Reveal
23
+ ```
24
+
25
+ Python 3.10+. `config.py` is gitignored so your real token stays on your
26
+ machine; `config.example.py` is the committed placeholder.
27
+
28
+ ## Quickstart
29
+
30
+ ```python
31
+ from eazydraw import EazyDraw # (the old `from eazydraw_api import EazyDraw` still works)
32
+
33
+ # Paste your bearer token from API Settings -> Reveal
34
+ ed = EazyDraw(token="7b3f...")
35
+
36
+ ed.status()
37
+ # {'status': 'OK', 'version': '12.3.6', 'build': '51059'}
38
+
39
+ # Open a drawing, capture its uuid
40
+ d = ed.open_drawing("~/Documents/sketch.ezdjson")
41
+ print(d["uuid"], "newly opened" if ed.last_response.status_code == 201 else "already open")
42
+
43
+ # Walk down to graphics on the active layer
44
+ layers = ed.layers(d["uuid"])
45
+ gs = ed.layer_graphics(d["uuid"], layers[0]["uuid"])
46
+ print(len(gs), "graphics on layer 0")
47
+
48
+ # Place a library element into the drawing
49
+ libs = ed.libraries()
50
+ math_lib = next(l for l in libs if l["DisplayName"] == "Math")
51
+ els = ed.library_elements(math_lib["uuid"])
52
+ graphic_el = next(e for e in els if e["elementType"] == "graphic")
53
+ placed = ed.use_library_element(
54
+ d["uuid"], layers[0]["uuid"], math_lib["uuid"], graphic_el["uuid"]
55
+ )
56
+ print("Placed", placed["class"], "as", placed["graphicUUID"])
57
+
58
+ # Export the drawing as a PNG to disk
59
+ ed.export_drawing(d["uuid"], "png", save_to="/tmp/sketch.png")
60
+
61
+ # Close it
62
+ ed.close_drawing(d["uuid"])
63
+ ```
64
+
65
+ ## Typed models (optional)
66
+
67
+ The client returns raw dicts. For validation / IDE support, parse them with the
68
+ pydantic models in `eazydraw.models` (these mirror `spec/openapi.yaml`):
69
+
70
+ ```python
71
+ from eazydraw import EazyDraw, Graphic, Text
72
+
73
+ ed = EazyDraw(token="7b3f...")
74
+ g = Graphic.model_validate(ed.graphic(D, L, G))
75
+ print(g.graphic_uuid, g.hidden_bounds.width, g.is_group) # snake_case fields
76
+
77
+ # round-trips back to wire form (camelCase / PascalCase keys)
78
+ g.model_dump(by_alias=True)
79
+ ```
80
+
81
+ Field names are snake_case with the wire keys as aliases; unknown server keys
82
+ are preserved (`extra="allow"`), so a newer server field won't break parsing.
83
+
84
+ ## Error handling
85
+
86
+ Non-2xx responses raise `EazyDrawError(status_code, message, body)`:
87
+
88
+ ```python
89
+ from eazydraw import EazyDraw, EazyDrawError
90
+
91
+ ed = EazyDraw(token="wrong-token")
92
+ try:
93
+ ed.status()
94
+ except EazyDrawError as exc:
95
+ print(exc.status_code, exc.message) # 401 "Missing or invalid bearer token"
96
+ ```
97
+
98
+ ## Status codes
99
+
100
+ Every method calls `self.last_response = resp`, so when the server's status
101
+ code carries semantic meaning (201 vs 200 on POST, etc.), inspect it
102
+ afterward:
103
+
104
+ ```python
105
+ d = ed.open_drawing(path)
106
+ already_open = (ed.last_response.status_code == 200)
107
+ ```
108
+
109
+ ## Conventions
110
+
111
+ - Methods take UUIDs as positional strings, in path order: `ed.layer(D, L)`,
112
+ `ed.layer_graphics(D, L)`, etc.
113
+ - Recursive group-traversal methods take a variadic list of UUIDs in chain
114
+ order: `ed.group_graphics(D, L, G1, G2, G3)` reads as "drilling down".
115
+ - `export_*` methods take `fmt` as a keyword argument (so the call site stays
116
+ readable when there's a long UUID chain) and accept an optional `save_to`
117
+ path; if given, bytes are written to that path and the path is returned.
118
+ Otherwise raw bytes are returned.
119
+ - Collection endpoints unwrap the envelope: `ed.drawings()` returns the list
120
+ directly, not `{"drawings": [...]}`.
121
+ - `client.py` is single-class; as we add API endpoints we add a method — no
122
+ inheritance, no abstractions. `models.py` is plain pydantic data classes.
123
+
124
+ ## Not shipped
125
+
126
+ This lives in the repo for convenience but is not part of the EazyDraw app
127
+ bundle. A versioned, published Python SDK is a future product decision; the
128
+ `eazydraw` package here is the working basis for it (and for the semantic
129
+ resolver + MCP server layers to come).
@@ -0,0 +1,62 @@
1
+ """
2
+ eazydraw — Python client + models for the EazyDraw Automation API.
3
+
4
+ from eazydraw import EazyDraw, Graphic
5
+ ed = EazyDraw(token="...")
6
+ g = Graphic.model_validate(ed.graphic(d, l, g_uuid))
7
+
8
+ The HTTP layer (``EazyDraw``, ``EazyDrawError``) lives in ``eazydraw.client``;
9
+ the typed JSON shapes in ``eazydraw.models``. The client returns raw dicts —
10
+ the models are an optional typed layer over them (see models.py).
11
+ """
12
+
13
+ from .client import EazyDraw, EazyDrawError, DEFAULT_HOST, DEFAULT_PORT
14
+ from .models import (
15
+ Status,
16
+ Rect,
17
+ Size,
18
+ Locks,
19
+ Drawing,
20
+ Library,
21
+ Layer,
22
+ Graphic,
23
+ Element,
24
+ TextLayout,
25
+ TextBase,
26
+ Text,
27
+ Annotation,
28
+ )
29
+ from .semantic import (
30
+ Semantic,
31
+ Located,
32
+ SemanticError,
33
+ NoMatch,
34
+ AmbiguousMatch,
35
+ )
36
+
37
+ __version__ = "1.0.0"
38
+
39
+ __all__ = [
40
+ "EazyDraw",
41
+ "EazyDrawError",
42
+ "DEFAULT_HOST",
43
+ "DEFAULT_PORT",
44
+ "Status",
45
+ "Rect",
46
+ "Size",
47
+ "Locks",
48
+ "Drawing",
49
+ "Library",
50
+ "Layer",
51
+ "Graphic",
52
+ "Element",
53
+ "TextLayout",
54
+ "TextBase",
55
+ "Text",
56
+ "Annotation",
57
+ "Semantic",
58
+ "Located",
59
+ "SemanticError",
60
+ "NoMatch",
61
+ "AmbiguousMatch",
62
+ ]