ducpy 3.3.8__tar.gz → 3.4.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.
Files changed (59) hide show
  1. {ducpy-3.3.8 → ducpy-3.4.0}/Cargo.lock +1 -1
  2. ducpy-3.4.0/PKG-INFO +104 -0
  3. ducpy-3.4.0/README.md +87 -0
  4. {ducpy-3.3.8 → ducpy-3.4.0}/packages/ducpy/crate/Cargo.toml +1 -1
  5. ducpy-3.4.0/packages/ducrs/README.md +54 -0
  6. {ducpy-3.3.8 → ducpy-3.4.0}/src/ducpy/builders/sql_builder.py +118 -14
  7. {ducpy-3.3.8 → ducpy-3.4.0}/src/ducpy/serialize.py +45 -1
  8. ducpy-3.3.8/PKG-INFO +0 -83
  9. ducpy-3.3.8/README.md +0 -65
  10. ducpy-3.3.8/packages/ducrs/README.md +0 -52
  11. {ducpy-3.3.8 → ducpy-3.4.0}/Cargo.toml +0 -0
  12. {ducpy-3.3.8 → ducpy-3.4.0}/LICENSE +0 -0
  13. {ducpy-3.3.8 → ducpy-3.4.0}/packages/ducpy/crate/src/lib.rs +0 -0
  14. {ducpy-3.3.8 → ducpy-3.4.0}/packages/ducrs/Cargo.toml +0 -0
  15. {ducpy-3.3.8 → ducpy-3.4.0}/packages/ducrs/LICENSE +0 -0
  16. {ducpy-3.3.8 → ducpy-3.4.0}/packages/ducrs/build.rs +0 -0
  17. {ducpy-3.3.8 → ducpy-3.4.0}/packages/ducrs/schema/duc.sql +0 -0
  18. {ducpy-3.3.8 → ducpy-3.4.0}/packages/ducrs/schema/migrations/3000000_to_3000001.sql +0 -0
  19. {ducpy-3.3.8 → ducpy-3.4.0}/packages/ducrs/schema/migrations/3000001_to_3000002.sql +0 -0
  20. {ducpy-3.3.8 → ducpy-3.4.0}/packages/ducrs/schema/migrations/3000002_to_3000003.sql +0 -0
  21. {ducpy-3.3.8 → ducpy-3.4.0}/packages/ducrs/schema/migrations/3000003_to_3000004.sql +0 -0
  22. {ducpy-3.3.8 → ducpy-3.4.0}/packages/ducrs/schema/search.sql +0 -0
  23. {ducpy-3.3.8 → ducpy-3.4.0}/packages/ducrs/schema/version_control.sql +0 -0
  24. {ducpy-3.3.8 → ducpy-3.4.0}/packages/ducrs/src/api/document.rs +0 -0
  25. {ducpy-3.3.8 → ducpy-3.4.0}/packages/ducrs/src/api/meta.rs +0 -0
  26. {ducpy-3.3.8 → ducpy-3.4.0}/packages/ducrs/src/api/mod.rs +0 -0
  27. {ducpy-3.3.8 → ducpy-3.4.0}/packages/ducrs/src/api/version_control.rs +0 -0
  28. {ducpy-3.3.8 → ducpy-3.4.0}/packages/ducrs/src/db/bootstrap.rs +0 -0
  29. {ducpy-3.3.8 → ducpy-3.4.0}/packages/ducrs/src/db/mod.rs +0 -0
  30. {ducpy-3.3.8 → ducpy-3.4.0}/packages/ducrs/src/db/native.rs +0 -0
  31. {ducpy-3.3.8 → ducpy-3.4.0}/packages/ducrs/src/db/wasm.rs +0 -0
  32. {ducpy-3.3.8 → ducpy-3.4.0}/packages/ducrs/src/lib.rs +0 -0
  33. {ducpy-3.3.8 → ducpy-3.4.0}/packages/ducrs/src/parse.rs +0 -0
  34. {ducpy-3.3.8 → ducpy-3.4.0}/packages/ducrs/src/serde_utils.rs +0 -0
  35. {ducpy-3.3.8 → ducpy-3.4.0}/packages/ducrs/src/serialize.rs +0 -0
  36. {ducpy-3.3.8 → ducpy-3.4.0}/packages/ducrs/src/types.rs +0 -0
  37. {ducpy-3.3.8 → ducpy-3.4.0}/pyproject.toml +0 -0
  38. {ducpy-3.3.8 → ducpy-3.4.0}/src/ducpy/__init__.py +0 -0
  39. {ducpy-3.3.8 → ducpy-3.4.0}/src/ducpy/builders/__init__.py +0 -0
  40. {ducpy-3.3.8 → ducpy-3.4.0}/src/ducpy/builders/block_instance_builder.py +0 -0
  41. {ducpy-3.3.8 → ducpy-3.4.0}/src/ducpy/builders/block_utils.py +0 -0
  42. {ducpy-3.3.8 → ducpy-3.4.0}/src/ducpy/builders/element_builders.py +0 -0
  43. {ducpy-3.3.8 → ducpy-3.4.0}/src/ducpy/builders/mutate_builder.py +0 -0
  44. {ducpy-3.3.8 → ducpy-3.4.0}/src/ducpy/builders/state_builders.py +0 -0
  45. {ducpy-3.3.8 → ducpy-3.4.0}/src/ducpy/builders/style_builders.py +0 -0
  46. {ducpy-3.3.8 → ducpy-3.4.0}/src/ducpy/classes/DataStateClass.py +0 -0
  47. {ducpy-3.3.8 → ducpy-3.4.0}/src/ducpy/classes/ElementsClass.py +0 -0
  48. {ducpy-3.3.8 → ducpy-3.4.0}/src/ducpy/classes/__init__.py +0 -0
  49. {ducpy-3.3.8 → ducpy-3.4.0}/src/ducpy/enums.py +0 -0
  50. {ducpy-3.3.8 → ducpy-3.4.0}/src/ducpy/parse.py +0 -0
  51. {ducpy-3.3.8 → ducpy-3.4.0}/src/ducpy/search/__init__.py +0 -0
  52. {ducpy-3.3.8 → ducpy-3.4.0}/src/ducpy/search/search_elements.py +0 -0
  53. {ducpy-3.3.8 → ducpy-3.4.0}/src/ducpy/utils/__init__.py +0 -0
  54. {ducpy-3.3.8 → ducpy-3.4.0}/src/ducpy/utils/constants.py +0 -0
  55. {ducpy-3.3.8 → ducpy-3.4.0}/src/ducpy/utils/convert.py +0 -0
  56. {ducpy-3.3.8 → ducpy-3.4.0}/src/ducpy/utils/io.py +0 -0
  57. {ducpy-3.3.8 → ducpy-3.4.0}/src/ducpy/utils/mutate_utils.py +0 -0
  58. {ducpy-3.3.8 → ducpy-3.4.0}/src/ducpy/utils/rand_utils.py +0 -0
  59. {ducpy-3.3.8 → ducpy-3.4.0}/src/ducpy_native/__init__.py +0 -0
@@ -412,7 +412,7 @@ dependencies = [
412
412
 
413
413
  [[package]]
414
414
  name = "ducpy-native"
415
- version = "3.3.8"
415
+ version = "3.4.0"
416
416
  dependencies = [
417
417
  "duc",
418
418
  "pyo3",
ducpy-3.4.0/PKG-INFO ADDED
@@ -0,0 +1,104 @@
1
+ Metadata-Version: 2.4
2
+ Name: ducpy
3
+ Version: 3.4.0
4
+ Classifier: License :: OSI Approved :: MIT License
5
+ Requires-Dist: nanoid>=2.0.0
6
+ License-File: LICENSE
7
+ Summary: The library for the Duc 2D CAD file format
8
+ Author: Jorge Soares
9
+ Author-email: Ducflair <support@ducflair.com>
10
+ License: MIT
11
+ Requires-Python: >=3.10
12
+ Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
13
+ Project-URL: Documentation, https://ducflair.github.io/duc/reference/python/
14
+ Project-URL: Homepage, https://duc.ducflair.com
15
+ Project-URL: Package Index, https://ducflair.github.io/duc/simple/ducpy/
16
+ Project-URL: Source, https://github.com/ducflair/duc/tree/main/packages/ducpy
17
+
18
+ # ducpy
19
+
20
+ <p align="center">
21
+ <br/>
22
+ <a href="https://duc.ducflair.com" target="_blank"><img width="256px" src="https://cdn.jsdelivr.net/gh/ducflair/assets@main/src/duc/duc-extended.png" /></a>
23
+ <p align="center">2D CAD File Format</p>
24
+ <p align="center" style="align: center;">
25
+ <a href="https://pypi.org/project/ducpy/"><img src="https://shields.io/badge/Pip-blue?logo=Pypi&logoColor=white&style=round-square" alt="Pip" /></a>
26
+ <a href="https://github.com/ducflair/duc/releases"><img src="https://img.shields.io/pypi/v/ducpy?style=round-square&label=latest%20stable" alt="PyPI ducpy@latest release" /></a>
27
+ <a href="https://pypi.org/project/ducpy/"><img src="https://img.shields.io/pypi/dm/ducpy?style=round-square&color=salmon" alt="Downloads" /></a>
28
+ <img src="https://shields.io/badge/Python-ffde57?logo=Python&logoColor=646464&style=round-square" alt="Python" />
29
+ </p>
30
+ </p>
31
+
32
+ The `ducpy` package is the official Python implementation of the `.duc` 2D CAD file format. It is built on top of the [`duc`](../ducrs) Rust crate, exposed as the `ducpy_native` extension. A `.duc` file is a zlib-compressed SQLite database — `ducpy` gives you both a high-level builder DSL and direct low-level access to that schema, plus parsing, serialization, and search helpers.
33
+
34
+ ## Installation
35
+
36
+ ```bash
37
+ pip install ducpy
38
+ ```
39
+
40
+ or with [`uv`](https://docs.astral.sh/uv/):
41
+
42
+ ```bash
43
+ uv add ducpy
44
+ ```
45
+
46
+ ## Quick start
47
+
48
+ ```python
49
+ import ducpy as duc
50
+ ```
51
+
52
+ ## API overview
53
+
54
+ `ducpy` is organised around four conceptual entry points, matching the structure of the full [API reference](https://duc.ducflair.com):
55
+
56
+ ### Builders API (High-level)
57
+
58
+ The easy way to build and manage `.duc` files. Construct elements, apply styles, manage layers, build blocks, and handle document state through the `duc.builders` module.
59
+
60
+ See the worked examples:
61
+
62
+ - [Element creation](src/examples/element_creation_demo.py) — building rectangles, ellipses, polygons, lines, arrows, text, frames and plots with the fluent builder DSL.
63
+ - [Mutating elements](src/examples/mutation_demo.py) — updating element properties in place and observing version changes.
64
+ - [Document elements](src/examples/document_element_demo.py) and [model elements](src/examples/model_element_demo.py) — for the higher-level container / model element types.
65
+
66
+ ### SQL Builder (Low-level)
67
+
68
+ A `.duc` file is a zlib-compressed SQLite database. Use `duc.builders.sql_builder` (`DucSQL`) for direct schema access, bulk queries, and low-level manipulation.
69
+
70
+ See the worked example: [SQL Builder](src/examples/sql_builder_demo.py) — `DucSQL.new()` to create a `.duc` from scratch, `DucSQL(path)` to query an existing one.
71
+
72
+ ### Search
73
+
74
+ Query and search elements and files programmatically through the `duc.search` API.
75
+
76
+ ### File I/O
77
+
78
+ Read and write `.duc` files using the `duc.parse` and `duc.serialize` modules.
79
+
80
+ See the worked examples:
81
+
82
+ - [Parsing](src/examples/parsing_demo.py) — `parse_duc` / `parse_duc_lazy` / `get_external_file` / `list_external_files`.
83
+ - [Serialization](src/examples/serialization_demo.py) — `serialize_duc` from builder-created elements.
84
+ - [External files](src/examples/external_files_demo.py) — attaching binary blobs (images, PDFs) to a `.duc` document.
85
+
86
+ ## Documentation
87
+
88
+ For detailed documentation, including the full API reference, schema specification, and end-to-end examples, see:
89
+
90
+ - API reference & guides: [duc.ducflair.com](https://duc.ducflair.com)
91
+ - Source-level reference: [docs/](docs/index.rst)
92
+ - Full example index: [docs/examples.rst](docs/examples.rst)
93
+
94
+ ## Tools
95
+
96
+ - [Playground](https://scopture.com): Experiment with the `duc` format in a live environment.
97
+
98
+ ## Contributing
99
+
100
+ Feel free to open an issue or contact us at [Ducflair Support](https://www.ducflair.com/support).
101
+
102
+ ## License
103
+
104
+ This project is licensed under the MIT License. See the [LICENSE](./LICENSE) file for more details.
ducpy-3.4.0/README.md ADDED
@@ -0,0 +1,87 @@
1
+ # ducpy
2
+
3
+ <p align="center">
4
+ <br/>
5
+ <a href="https://duc.ducflair.com" target="_blank"><img width="256px" src="https://cdn.jsdelivr.net/gh/ducflair/assets@main/src/duc/duc-extended.png" /></a>
6
+ <p align="center">2D CAD File Format</p>
7
+ <p align="center" style="align: center;">
8
+ <a href="https://pypi.org/project/ducpy/"><img src="https://shields.io/badge/Pip-blue?logo=Pypi&logoColor=white&style=round-square" alt="Pip" /></a>
9
+ <a href="https://github.com/ducflair/duc/releases"><img src="https://img.shields.io/pypi/v/ducpy?style=round-square&label=latest%20stable" alt="PyPI ducpy@latest release" /></a>
10
+ <a href="https://pypi.org/project/ducpy/"><img src="https://img.shields.io/pypi/dm/ducpy?style=round-square&color=salmon" alt="Downloads" /></a>
11
+ <img src="https://shields.io/badge/Python-ffde57?logo=Python&logoColor=646464&style=round-square" alt="Python" />
12
+ </p>
13
+ </p>
14
+
15
+ The `ducpy` package is the official Python implementation of the `.duc` 2D CAD file format. It is built on top of the [`duc`](../ducrs) Rust crate, exposed as the `ducpy_native` extension. A `.duc` file is a zlib-compressed SQLite database — `ducpy` gives you both a high-level builder DSL and direct low-level access to that schema, plus parsing, serialization, and search helpers.
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ pip install ducpy
21
+ ```
22
+
23
+ or with [`uv`](https://docs.astral.sh/uv/):
24
+
25
+ ```bash
26
+ uv add ducpy
27
+ ```
28
+
29
+ ## Quick start
30
+
31
+ ```python
32
+ import ducpy as duc
33
+ ```
34
+
35
+ ## API overview
36
+
37
+ `ducpy` is organised around four conceptual entry points, matching the structure of the full [API reference](https://duc.ducflair.com):
38
+
39
+ ### Builders API (High-level)
40
+
41
+ The easy way to build and manage `.duc` files. Construct elements, apply styles, manage layers, build blocks, and handle document state through the `duc.builders` module.
42
+
43
+ See the worked examples:
44
+
45
+ - [Element creation](src/examples/element_creation_demo.py) — building rectangles, ellipses, polygons, lines, arrows, text, frames and plots with the fluent builder DSL.
46
+ - [Mutating elements](src/examples/mutation_demo.py) — updating element properties in place and observing version changes.
47
+ - [Document elements](src/examples/document_element_demo.py) and [model elements](src/examples/model_element_demo.py) — for the higher-level container / model element types.
48
+
49
+ ### SQL Builder (Low-level)
50
+
51
+ A `.duc` file is a zlib-compressed SQLite database. Use `duc.builders.sql_builder` (`DucSQL`) for direct schema access, bulk queries, and low-level manipulation.
52
+
53
+ See the worked example: [SQL Builder](src/examples/sql_builder_demo.py) — `DucSQL.new()` to create a `.duc` from scratch, `DucSQL(path)` to query an existing one.
54
+
55
+ ### Search
56
+
57
+ Query and search elements and files programmatically through the `duc.search` API.
58
+
59
+ ### File I/O
60
+
61
+ Read and write `.duc` files using the `duc.parse` and `duc.serialize` modules.
62
+
63
+ See the worked examples:
64
+
65
+ - [Parsing](src/examples/parsing_demo.py) — `parse_duc` / `parse_duc_lazy` / `get_external_file` / `list_external_files`.
66
+ - [Serialization](src/examples/serialization_demo.py) — `serialize_duc` from builder-created elements.
67
+ - [External files](src/examples/external_files_demo.py) — attaching binary blobs (images, PDFs) to a `.duc` document.
68
+
69
+ ## Documentation
70
+
71
+ For detailed documentation, including the full API reference, schema specification, and end-to-end examples, see:
72
+
73
+ - API reference & guides: [duc.ducflair.com](https://duc.ducflair.com)
74
+ - Source-level reference: [docs/](docs/index.rst)
75
+ - Full example index: [docs/examples.rst](docs/examples.rst)
76
+
77
+ ## Tools
78
+
79
+ - [Playground](https://scopture.com): Experiment with the `duc` format in a live environment.
80
+
81
+ ## Contributing
82
+
83
+ Feel free to open an issue or contact us at [Ducflair Support](https://www.ducflair.com/support).
84
+
85
+ ## License
86
+
87
+ This project is licensed under the MIT License. See the [LICENSE](./LICENSE) file for more details.
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "ducpy-native"
3
- version = "3.3.8"
3
+ version = "3.4.0"
4
4
  edition = "2021"
5
5
  description = "Python native extension for the duc file format (used by ducpy)"
6
6
  publish = false
@@ -0,0 +1,54 @@
1
+ # duc (Rust)
2
+
3
+ <p align="center">
4
+ <br/>
5
+ <a href="https://duc.ducflair.com" target="_blank"><img width="256px" src="https://cdn.jsdelivr.net/gh/ducflair/assets@main/src/duc/duc-extended.png" /></a>
6
+ <p align="center">2D CAD File Format</p>
7
+ <p align="center" style="align: center;">
8
+ <a href="https://crates.io/crates/duc"><img src="https://shields.io/badge/Crates-FFC933?logo=Rust&logoColor=646464&style=round-square" alt="Crates" /></a>
9
+ <a href="https://github.com/ducflair/duc/releases"><img src="https://img.shields.io/crates/v/duc?style=round-square&label=latest%20stable" alt="crates.io duc@latest release" /></a>
10
+ <a href="https://crates.io/crates/duc"><img src="https://img.shields.io/crates/d/duc?style=round-square&color=salmon" alt="Downloads" /></a>
11
+ <img src="https://shields.io/badge/Rust-CE412B?logo=Rust&logoColor=fff&style=round-square" alt="Rust" />
12
+ </p>
13
+ </p>
14
+
15
+ The `duc` crate is the canonical Rust implementation of the `.duc` 2D CAD file format and the **root of the entire Duc ecosystem**. [`ducpy`](https://pypi.org/project/ducpy/), [`ducjs`](https://www.npmjs.com/package/ducjs), and the rest of the toolchain are all built on top of the types, parsers and storage layer defined here.
16
+
17
+ A `.duc` file is a standard zlib compressed **SQLite database file** following the schema in `schema/duc.sql`. This crate wraps that database in a strongly-typed Rust API for reading, writing and manipulating `.duc` documents.
18
+
19
+
20
+ ----
21
+ <br/>
22
+ <br/>
23
+ <br/>
24
+
25
+ ## Schema and migrations
26
+
27
+ The canonical SQL lives at `schema/duc.sql` (plus `schema/version_control.sql` and `schema/search.sql`) and is **embedded at compile time** by [`build.rs`](build.rs)
28
+
29
+ Migrations are ran and handled **automatically** upon usage of the library. Each migration is a SQL file named `<from>_to_<to>.sql` in `schema/migrations/`. `build.rs` scans that directory, sorts by `from_version` and emits a static `MIGRATIONS` array. Adding a new migration requires **no Rust changes** — drop the file in and rebuild.
30
+
31
+ ## Features
32
+
33
+ | Feature | Default | Description |
34
+ |---------|---------|-------------|
35
+ | `default` | ✅ | Native build with bundled SQLite |
36
+ | `opfs` | ❌ | OPFS SAH-pool VFS for `wasm32-unknown-unknown` (browser persistence) |
37
+
38
+
39
+ ## Documentation
40
+
41
+ - Full API reference: [docs.rs/duc](https://docs.rs/duc)
42
+ - Format specification, concepts and guides: [duc.ducflair.com](https://duc.ducflair.com)
43
+
44
+ ## Tools
45
+
46
+ - [Playground](https://ducflair.com/core): Experiment with the `.duc` format in the browser.
47
+
48
+ ## Contributing
49
+
50
+ Feel free to open an issue or contact us at [Ducflair Support](https://www.ducflair.com/support).
51
+
52
+ ## License
53
+
54
+ This project is licensed under the MIT License. See the [LICENSE](./LICENSE) file for more details.
@@ -31,12 +31,71 @@ from __future__ import annotations
31
31
  import os
32
32
  import sqlite3
33
33
  import tempfile
34
+ import zlib
34
35
  from pathlib import Path
35
- from typing import Any, List, Optional, Union
36
+ from typing import Any, List, Optional, Sequence, Union
36
37
 
37
38
  import ducpy_native
38
39
 
39
- __all__ = ["DucSQL"]
40
+ __all__ = ["DucSQL", "quote_sql_identifier"]
41
+ SQLITE_HEADER_MAGIC = b"SQLite format 3\x00"
42
+
43
+
44
+ def quote_sql_identifier(value: str) -> str:
45
+ """Quote a SQLite identifier such as an attached database alias."""
46
+ if not value:
47
+ raise ValueError("SQLite identifier cannot be empty.")
48
+ return '"' + value.replace('"', '""') + '"'
49
+
50
+
51
+ def _is_sqlite_bytes(data: bytes) -> bool:
52
+ return data.startswith(SQLITE_HEADER_MAGIC)
53
+
54
+
55
+ def _normalize_sqlite_image(data: bytes) -> bytes:
56
+ image = bytearray(data)
57
+ if len(image) > 19:
58
+ if image[18] == 2:
59
+ image[18] = 1
60
+ if image[19] == 2:
61
+ image[19] = 1
62
+ return bytes(image)
63
+
64
+
65
+ def _inflate_duc_bytes(data: bytes) -> bytes:
66
+ if _is_sqlite_bytes(data):
67
+ return _normalize_sqlite_image(data)
68
+ inflated = zlib.decompress(data, -zlib.MAX_WBITS)
69
+ if not _is_sqlite_bytes(inflated):
70
+ raise ValueError("decompressed .duc payload does not start with a SQLite header")
71
+ return _normalize_sqlite_image(inflated)
72
+
73
+
74
+ def _deflate_duc_bytes(data: bytes) -> bytes:
75
+ compressor = zlib.compressobj(level=-1, wbits=-zlib.MAX_WBITS)
76
+ return compressor.compress(data) + compressor.flush()
77
+
78
+
79
+ def _write_temp_sqlite(data: bytes) -> str:
80
+ tmp = tempfile.NamedTemporaryFile(suffix=".duc", delete=False)
81
+ try:
82
+ tmp.write(_inflate_duc_bytes(data))
83
+ tmp.close()
84
+ return tmp.name
85
+ except Exception:
86
+ tmp.close()
87
+ os.unlink(tmp.name)
88
+ raise
89
+
90
+
91
+ def _sqlite_path_for_duc(path: Union[str, Path]) -> tuple[str, Optional[str]]:
92
+ path = str(path)
93
+ with open(path, "rb") as f:
94
+ data = f.read()
95
+ if _is_sqlite_bytes(data):
96
+ return path, None
97
+ temp_path = _write_temp_sqlite(data)
98
+ return temp_path, temp_path
40
99
 
41
100
 
42
101
  def _get_current_schema_version() -> int:
@@ -105,12 +164,14 @@ class DucSQL:
105
164
  path = str(path)
106
165
  if not os.path.exists(path):
107
166
  raise FileNotFoundError(f"File not found: {path}")
108
- self.conn: sqlite3.Connection = sqlite3.connect(path)
167
+ sqlite_path, temp_path = _sqlite_path_for_duc(path)
168
+ self.conn: sqlite3.Connection = sqlite3.connect(sqlite_path)
109
169
  self.conn.row_factory = sqlite3.Row
110
170
  _apply_pragmas(self.conn)
111
171
  _apply_migrations(self.conn)
112
- self._path: Optional[str] = path
113
- self._temp: Optional[str] = None
172
+ self._path: Optional[str] = sqlite_path
173
+ self._temp: Optional[str] = temp_path
174
+ self._attached_temps: list[str] = []
114
175
  self._closed = False
115
176
 
116
177
  @classmethod
@@ -126,29 +187,68 @@ class DucSQL:
126
187
  inst.conn.executescript(_read_schema_sql())
127
188
  inst._path = target if path else None
128
189
  inst._temp = None
190
+ inst._attached_temps = []
129
191
  inst._closed = False
130
192
  return inst
131
193
 
132
194
  @classmethod
133
195
  def from_bytes(cls, data: bytes) -> DucSQL:
134
196
  """Open a ``.duc`` from raw bytes (temp file, cleaned up on close)."""
135
- tmp = tempfile.NamedTemporaryFile(suffix=".duc", delete=False)
197
+ temp_path = _write_temp_sqlite(data)
136
198
  try:
137
- tmp.write(data)
138
- tmp.close()
139
199
  inst = object.__new__(cls)
140
- inst.conn = sqlite3.connect(tmp.name)
200
+ inst.conn = sqlite3.connect(temp_path)
141
201
  inst.conn.row_factory = sqlite3.Row
142
202
  _apply_pragmas(inst.conn)
143
203
  _apply_migrations(inst.conn)
144
- inst._path = tmp.name
145
- inst._temp = tmp.name
204
+ inst._path = temp_path
205
+ inst._temp = temp_path
206
+ inst._attached_temps = []
146
207
  inst._closed = False
147
208
  return inst
148
209
  except Exception:
149
- os.unlink(tmp.name)
210
+ os.unlink(temp_path)
150
211
  raise
151
212
 
213
+ @classmethod
214
+ def attach_many(
215
+ cls,
216
+ paths: Sequence[Union[str, Path]],
217
+ aliases: Optional[Sequence[str]] = None,
218
+ read_only: bool = True,
219
+ ) -> DucSQL:
220
+ """Open an in-memory SQLite connection with multiple ``.duc`` files attached.
221
+
222
+ This is useful for querying multiple drawings in one SQL statement. The
223
+ returned ``DucSQL`` owns only the in-memory connection; attached files are
224
+ not deleted on close.
225
+ """
226
+ if aliases is not None and len(aliases) != len(paths):
227
+ raise ValueError("aliases must have the same length as paths.")
228
+
229
+ inst = object.__new__(cls)
230
+ inst.conn = sqlite3.connect(":memory:")
231
+ inst.conn.row_factory = sqlite3.Row
232
+ inst._path = None
233
+ inst._temp = None
234
+ inst._attached_temps = []
235
+ inst._closed = False
236
+
237
+ for index, path in enumerate(paths):
238
+ alias = aliases[index] if aliases is not None else f"d{index}"
239
+ sqlite_path, temp_path = _sqlite_path_for_duc(path)
240
+ if temp_path:
241
+ inst._attached_temps.append(temp_path)
242
+ inst.conn.execute(
243
+ f"ATTACH DATABASE ? AS {quote_sql_identifier(alias)}",
244
+ (sqlite_path,),
245
+ )
246
+
247
+ if read_only:
248
+ inst.conn.execute("PRAGMA query_only = ON")
249
+
250
+ return inst
251
+
152
252
  # ------------------------------------------------------------------
153
253
  # SQL execution
154
254
  # ------------------------------------------------------------------
@@ -186,7 +286,7 @@ class DucSQL:
186
286
  finally:
187
287
  dst.close()
188
288
 
189
- def to_bytes(self) -> bytes:
289
+ def to_bytes(self, compressed: bool = False) -> bytes:
190
290
  """Export the database as raw bytes."""
191
291
  self.commit()
192
292
  tmp = tempfile.NamedTemporaryFile(suffix=".duc", delete=False)
@@ -196,7 +296,8 @@ class DucSQL:
196
296
  self.conn.backup(dst)
197
297
  dst.close()
198
298
  with open(tmp.name, "rb") as f:
199
- return f.read()
299
+ data = f.read()
300
+ return _deflate_duc_bytes(data) if compressed else data
200
301
  finally:
201
302
  os.unlink(tmp.name)
202
303
 
@@ -210,6 +311,9 @@ class DucSQL:
210
311
  self._closed = True
211
312
  if self._temp and os.path.exists(self._temp):
212
313
  os.unlink(self._temp)
314
+ for temp_path in getattr(self, "_attached_temps", []):
315
+ if os.path.exists(temp_path):
316
+ os.unlink(temp_path)
213
317
 
214
318
  def __enter__(self) -> DucSQL:
215
319
  return self
@@ -5,11 +5,15 @@ Serialize DUC data using the Rust native extension (ducpy_native).
5
5
  from __future__ import annotations
6
6
 
7
7
  import logging
8
+ import builtins
9
+ import contextlib
10
+ import io
8
11
  import os
9
12
  import re
10
13
  import subprocess
11
14
  import sys
12
15
  import tempfile
16
+ import traceback
13
17
  from dataclasses import asdict, is_dataclass
14
18
  from pathlib import Path
15
19
  from typing import Any, Dict, List, Optional, Tuple
@@ -256,6 +260,8 @@ builtins.resolve_external_file = resolve_external_file
256
260
  )
257
261
  except subprocess.TimeoutExpired as exc:
258
262
  return f"{label}: Python validation timed out after {timeout_seconds:g}s"
263
+ except PermissionError:
264
+ return _run_python_validation_in_process(code, label, tmp_path, resolved_files)
259
265
  except OSError as exc:
260
266
  return f"{label}: failed to start Python validation: {exc}"
261
267
 
@@ -266,6 +272,44 @@ builtins.resolve_external_file = resolve_external_file
266
272
  return f"{label}: Python validation failed\n{output}"
267
273
 
268
274
 
275
+ def _run_python_validation_in_process(
276
+ code: str,
277
+ label: str,
278
+ project_dir: Path,
279
+ resolved_files: Dict[str, str],
280
+ ) -> Optional[str]:
281
+ previous_cwd = os.getcwd()
282
+ had_resolver = hasattr(builtins, "resolve_external_file")
283
+ previous_resolver = getattr(builtins, "resolve_external_file", None)
284
+
285
+ def resolve_external_file(file_id: str) -> str:
286
+ if file_id in resolved_files:
287
+ return resolved_files[file_id]
288
+ if callable(previous_resolver):
289
+ return os.fspath(previous_resolver(file_id))
290
+ raise FileNotFoundError(f"External file '{file_id}' not found in validation sandbox.")
291
+
292
+ try:
293
+ os.chdir(project_dir)
294
+ builtins.resolve_external_file = resolve_external_file
295
+ globals_dict = {
296
+ "__name__": "__main__",
297
+ "resolve_external_file": resolve_external_file,
298
+ }
299
+ with contextlib.redirect_stdout(io.StringIO()), contextlib.redirect_stderr(io.StringIO()):
300
+ exec(compile(code, "<ducpy-embedded-model>", "exec"), globals_dict)
301
+ except Exception:
302
+ return f"{label}: Python validation failed\n{traceback.format_exc().strip()}"
303
+ finally:
304
+ os.chdir(previous_cwd)
305
+ if had_resolver:
306
+ builtins.resolve_external_file = previous_resolver
307
+ elif hasattr(builtins, "resolve_external_file"):
308
+ delattr(builtins, "resolve_external_file")
309
+
310
+ return None
311
+
312
+
269
313
  def _write_typst_external_files(project_dir: Path, files_meta: Optional[Dict[str, Any]], files_data: Optional[Dict[str, bytes]]) -> None:
270
314
  if not files_meta or not files_data:
271
315
  return
@@ -324,7 +368,7 @@ def _validate_embedded_code(
324
368
  elements: List[Any],
325
369
  files_meta: Optional[Dict[str, Any]],
326
370
  files_data: Optional[Dict[str, bytes]],
327
- timeout_seconds: float,
371
+ timeout_seconds: Optional[float],
328
372
  ) -> None:
329
373
  failures: List[str] = []
330
374
 
ducpy-3.3.8/PKG-INFO DELETED
@@ -1,83 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: ducpy
3
- Version: 3.3.8
4
- Classifier: License :: OSI Approved :: MIT License
5
- Requires-Dist: nanoid>=2.0.0
6
- License-File: LICENSE
7
- Summary: The library for the Duc 2D CAD file format
8
- Author: Jorge Soares
9
- Author-email: Ducflair <support@ducflair.com>
10
- License: MIT
11
- Requires-Python: >=3.10
12
- Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
13
- Project-URL: Documentation, https://ducflair.github.io/duc/reference/python/
14
- Project-URL: Homepage, https://duc.ducflair.com
15
- Project-URL: Package Index, https://ducflair.github.io/duc/simple/ducpy/
16
- Project-URL: Source, https://github.com/ducflair/duc/tree/main/packages/ducpy
17
-
18
- # ducpy
19
-
20
- <p align="center">
21
- <br/>
22
- <a href="https://duc.ducflair.com" target="_blank"><img width="256px" src="https://cdn.jsdelivr.net/gh/ducflair/assets@main/src/duc/duc-extended.png" /></a>
23
- <p align="center">2D CAD File Format</p>
24
- <p align="center" style="align: center;">
25
- <a href="https://pypi.org/project/ducpy/"><img src="https://shields.io/badge/Pip-blue?logo=Pypi&logoColor=white&style=round-square" alt="Pip" /></a>
26
- <a href="https://github.com/ducflair/duc/releases"><img src="https://img.shields.io/pypi/v/ducpy?style=round-square&label=latest%20stable" alt="PyPI ducpy@latest release" /></a>
27
- <a href="https://pypi.org/project/ducpy/"><img src="https://img.shields.io/pypi/dm/ducpy?style=round-square&color=salmon" alt="Downloads" /></a>
28
- <img src="https://shields.io/badge/Python-ffde57?logo=Python&logoColor=646464&style=round-square" alt="Python" />
29
- </p>
30
- </p>
31
-
32
-
33
- The `ducpy` package provides a robust Python implementation for the `duc` 2D CAD file format, a cornerstone of our advanced design system. Designed for professionals seeking precision and efficiency, this package ensures seamless integration and manipulation of `duc` files within your Python projects.
34
-
35
- ## Introduction
36
-
37
- The `ducpy` package offers comprehensive Python types and helper functions to work effortlessly with the `duc` CAD file format. Built with efficiency and performance in mind, this package enables you to parse, validate, and manipulate `duc` files with ease.
38
-
39
- ## Features
40
-
41
- - **Python Support:** Strongly-typed interfaces for all aspects of the `duc` file format, ensuring type safety and IDE support.
42
- - **Utility Functions:** Easy-to-use functions for parsing, validating, and manipulating `duc` files.
43
- - **Extensible Architecture:** Designed to integrate seamlessly with other `ducflair` packages and your custom tools.
44
- - **Performance Optimized:** Efficient processing to handle complex CAD data with ease.
45
-
46
- ## Installation
47
-
48
- Install the package using pip:
49
-
50
- ```bash
51
- pip install ducpy
52
- ```
53
-
54
- ## Usage
55
-
56
- ```python
57
- import ducpy as duc
58
- ```
59
-
60
- ## Documentation
61
-
62
- For detailed documentation, including all available types and utility functions, visit our [Documentation](https://duc.ducflair.com).
63
-
64
- ## Tools
65
-
66
- - [Playground](https://scopture.com): Experiment with the `duc` format in a live environment.
67
-
68
- ## Contributing
69
-
70
- At the moment we are not accepting contributions to this package. However, we welcome feedback and suggestions for future improvements. Feel free to open an issue or contact us at [Ducflair Support](https://www.ducflair.com/support).
71
-
72
- ## License
73
-
74
- This project is licensed under the MIT License. See the [LICENSE](./LICENSE) file for more details.
75
-
76
- ## Commit Message Guidelines
77
-
78
- To ensure smooth releases with semantic-release, please follow [these guidelines](https://semantic-release.gitbook.io/semantic-release#how-does-it-work).
79
-
80
- ---
81
-
82
- *The duc format and libraries are constantly evolving, aiming to set new standards in the 2D CAD industry. Be a part of this transformation and help shape the future of design technology!*
83
-
ducpy-3.3.8/README.md DELETED
@@ -1,65 +0,0 @@
1
- # ducpy
2
-
3
- <p align="center">
4
- <br/>
5
- <a href="https://duc.ducflair.com" target="_blank"><img width="256px" src="https://cdn.jsdelivr.net/gh/ducflair/assets@main/src/duc/duc-extended.png" /></a>
6
- <p align="center">2D CAD File Format</p>
7
- <p align="center" style="align: center;">
8
- <a href="https://pypi.org/project/ducpy/"><img src="https://shields.io/badge/Pip-blue?logo=Pypi&logoColor=white&style=round-square" alt="Pip" /></a>
9
- <a href="https://github.com/ducflair/duc/releases"><img src="https://img.shields.io/pypi/v/ducpy?style=round-square&label=latest%20stable" alt="PyPI ducpy@latest release" /></a>
10
- <a href="https://pypi.org/project/ducpy/"><img src="https://img.shields.io/pypi/dm/ducpy?style=round-square&color=salmon" alt="Downloads" /></a>
11
- <img src="https://shields.io/badge/Python-ffde57?logo=Python&logoColor=646464&style=round-square" alt="Python" />
12
- </p>
13
- </p>
14
-
15
-
16
- The `ducpy` package provides a robust Python implementation for the `duc` 2D CAD file format, a cornerstone of our advanced design system. Designed for professionals seeking precision and efficiency, this package ensures seamless integration and manipulation of `duc` files within your Python projects.
17
-
18
- ## Introduction
19
-
20
- The `ducpy` package offers comprehensive Python types and helper functions to work effortlessly with the `duc` CAD file format. Built with efficiency and performance in mind, this package enables you to parse, validate, and manipulate `duc` files with ease.
21
-
22
- ## Features
23
-
24
- - **Python Support:** Strongly-typed interfaces for all aspects of the `duc` file format, ensuring type safety and IDE support.
25
- - **Utility Functions:** Easy-to-use functions for parsing, validating, and manipulating `duc` files.
26
- - **Extensible Architecture:** Designed to integrate seamlessly with other `ducflair` packages and your custom tools.
27
- - **Performance Optimized:** Efficient processing to handle complex CAD data with ease.
28
-
29
- ## Installation
30
-
31
- Install the package using pip:
32
-
33
- ```bash
34
- pip install ducpy
35
- ```
36
-
37
- ## Usage
38
-
39
- ```python
40
- import ducpy as duc
41
- ```
42
-
43
- ## Documentation
44
-
45
- For detailed documentation, including all available types and utility functions, visit our [Documentation](https://duc.ducflair.com).
46
-
47
- ## Tools
48
-
49
- - [Playground](https://scopture.com): Experiment with the `duc` format in a live environment.
50
-
51
- ## Contributing
52
-
53
- At the moment we are not accepting contributions to this package. However, we welcome feedback and suggestions for future improvements. Feel free to open an issue or contact us at [Ducflair Support](https://www.ducflair.com/support).
54
-
55
- ## License
56
-
57
- This project is licensed under the MIT License. See the [LICENSE](./LICENSE) file for more details.
58
-
59
- ## Commit Message Guidelines
60
-
61
- To ensure smooth releases with semantic-release, please follow [these guidelines](https://semantic-release.gitbook.io/semantic-release#how-does-it-work).
62
-
63
- ---
64
-
65
- *The duc format and libraries are constantly evolving, aiming to set new standards in the 2D CAD industry. Be a part of this transformation and help shape the future of design technology!*
@@ -1,52 +0,0 @@
1
- # duc (Rust)
2
-
3
- <p align="center">
4
- <br/>
5
- <a href="https://duc.ducflair.com" target="_blank"><img width="256px" src="https://cdn.jsdelivr.net/gh/ducflair/assets@main/src/duc/duc-extended.png" /></a>
6
- <p align="center">2D CAD File Format</p>
7
- <p align="center" style="align: center;">
8
- <a href="https://crates.io/crates/duc/"><img src="https://shields.io/badge/Crates-FFC933?logo=Rust&logoColor=646464&style=round-square" alt="Crates" /></a>
9
- <a href="https://github.com/ducflair/duc/releases"><img src="https://img.shields.io/crates/v/duc?style=round-square&label=latest%20stable" alt="Crates.io duc@latest release" /></a>
10
- <a href="https://crates.io/crates/duc"><img src="https://img.shields.io/crates/d/duc?style=round-square&color=salmon" alt="Downloads" /></a>
11
- <img src="https://shields.io/badge/Rust-CE412B?logo=Rust&logoColor=fff&style=round-square" alt="Rust" />
12
- </p>
13
- </p>
14
-
15
- The `duc` crate provides a robust Rust implementation for the `duc` 2D CAD file format, a cornerstone of our advanced design system. Designed for professionals seeking precision and efficiency, this crate ensures seamless integration and manipulation of `duc` files within your Rust projects.
16
-
17
- ## Introduction
18
-
19
- The `duc` crate offers comprehensive Rust types and helper functions to work effortlessly with the `duc` CAD file format. Built with efficiency and performance in mind, this crate enables you to parse, validate, and manipulate `duc` files with ease.
20
-
21
- ## Features
22
-
23
- - **Rust Support:** Strongly-typed interfaces for all aspects of the `duc` file format, ensuring type safety and IDE support.
24
- - **Utility Functions:** Easy-to-use functions for parsing, validating, and manipulating `duc` files.
25
- - **Extensible Architecture:** Designed to integrate seamlessly with other `ducflair` packages and your custom tools.
26
- - **Performance Optimized:** Efficient processing to handle complex CAD data with ease.
27
-
28
-
29
- ## Documentation
30
-
31
- For detailed documentation, including all available types and utility functions, visit our [Documentation](https://duc.ducflair.com).
32
-
33
- ## Tools
34
-
35
- - [Playground](https://ducflair.com/core): Experiment with the `duc` format in a live environment.
36
- - [Documentation](https://duc.ducflair.com): Comprehensive guides and API references.
37
-
38
- ## Contributing
39
-
40
- At the moment we are not accepting contributions to this crate. However, we welcome feedback and suggestions for future improvements. Feel free to open an issue or contact us at [Ducflair Support](https://www.ducflair.com/support).
41
-
42
- ## License
43
-
44
- This project is licensed under the MIT License. See the [LICENSE](./LICENSE) file for more details.
45
-
46
- ## Commit Message Guidelines
47
-
48
- To ensure smooth releases with semantic-release, please follow [these guidelines](https://semantic-release.gitbook.io/semantic-release#how-does-it-work).
49
-
50
- ---
51
-
52
- *The duc format and libraries are constantly evolving, aiming to set new standards in the 2D CAD industry. Be a part of this transformation and help shape the future of design technology!*
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes