partomatic 0.0.1__py3-none-any.whl

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.
partomatic/__init__.py ADDED
@@ -0,0 +1,3 @@
1
+ from .partomatic import Partomatic
2
+ from .buildable_part import BuildablePart
3
+ from .partomatic_config import PartomaticConfig
@@ -0,0 +1,39 @@
1
+ """BuildablePart is a dataclass that contains a Part object and additional inormation for saving and
2
+ displaying the part"""
3
+
4
+ from dataclasses import dataclass, field, fields, is_dataclass, MISSING
5
+ from pathlib import Path
6
+ from os import getcwd
7
+
8
+ from build123d import Part, Location
9
+
10
+
11
+ @dataclass
12
+ class BuildablePart(Part):
13
+ part: Part = field(default_factory=Part)
14
+ display_location: Location = field(default_factory=Location)
15
+ stl_folder: str = getcwd()
16
+ _file_name: str = "partomatic"
17
+
18
+ def __init__(self, part, file_name, **kwargs):
19
+ self.display_location = Location()
20
+ self.file_name = file_name
21
+ self.part = part
22
+ if "display_location" in kwargs:
23
+ display_location = kwargs["display_location"]
24
+ if isinstance(display_location, Location):
25
+ self.display_location = display_location
26
+ if "stl_folder" in kwargs:
27
+ self.stl_folder = kwargs["stl_folder"]
28
+
29
+ @property
30
+ def file_name(self) -> str:
31
+ return self._file_name
32
+
33
+ @file_name.setter
34
+ def file_name(self, value: str):
35
+ """
36
+ Assigns the file name to the BuildablePart, ensuring that no
37
+ file extension is included.
38
+ """
39
+ self._file_name = Path(value).stem
@@ -0,0 +1,124 @@
1
+ """Part extended for CI/CD automation"""
2
+
3
+ from dataclasses import dataclass, field, fields, is_dataclass, MISSING
4
+ from abc import ABC, abstractmethod
5
+ from pathlib import Path
6
+
7
+ from build123d import Part, Location, export_stl
8
+
9
+ import ocp_vscode
10
+
11
+ import yaml
12
+
13
+ from .partomatic_config import PartomaticConfig
14
+ from .buildable_part import BuildablePart
15
+
16
+
17
+ class Partomatic(ABC):
18
+ """
19
+ Partomatic is an extension of the Compound class from build123d
20
+ that allows for automation within a continuous integration
21
+ environment. Descendant classes must implement:
22
+ - compile: generating the geometry of components in the parts list
23
+ """
24
+
25
+ _config: PartomaticConfig
26
+ parts: list[BuildablePart] = field(default_factory=list)
27
+
28
+ @abstractmethod
29
+ def compile(self):
30
+ """
31
+ Builds the relevant parts for the partomatic part
32
+ """
33
+
34
+ def display(self):
35
+ """
36
+ Shows the relevant parts in OCP CAD Viewer
37
+ """
38
+ ocp_vscode.show(
39
+ (
40
+ [
41
+ part.part.move(Location(part.display_location))
42
+ for part in self.parts
43
+ ]
44
+ ),
45
+ reset_camera=ocp_vscode.Camera.KEEP,
46
+ )
47
+
48
+ def complete_stl_file_path(self, part: BuildablePart) -> str:
49
+ return str(
50
+ Path(
51
+ Path(part.stl_folder)
52
+ / f"{self._config.file_prefix}{part.file_name}{self._config.file_suffix}"
53
+ ).with_suffix(".stl")
54
+ )
55
+
56
+ def export_stls(self):
57
+ """
58
+ Generates the relevant STLs in the configured
59
+ folder
60
+ """
61
+ if self._config.stl_folder == "NONE":
62
+ return
63
+ for part in self.parts:
64
+ Path(self.complete_stl_file_path(part)).parent.mkdir(
65
+ parents=True, exist_ok=self._config.create_folders_if_missing
66
+ )
67
+ if (
68
+ not Path(self.complete_stl_file_path(part)).parent.exists()
69
+ or not Path(self.complete_stl_file_path(part)).parent.is_dir()
70
+ ):
71
+ raise FileNotFoundError(
72
+ f"Directory {Path(self.complete_stl_file_path(part)).parent} does not exist"
73
+ )
74
+ export_stl(part.part, self.complete_stl_file_path(part))
75
+
76
+ def load_config(self, configuration: any, **kwargs):
77
+ """
78
+ loads a partomatic configuration from a file or valid yaml
79
+ -------
80
+ arguments:
81
+ - configuration: the path to the configuration file
82
+ OR
83
+ a valid yaml configuration string
84
+ -------
85
+ notes:
86
+ if yaml_tree is set in the PartomaticConfig descendent,
87
+ PartomaticConfig will use that tree to find a node deep
88
+ within the yaml tree, following the node names separated by slashes
89
+ (example: "BigObject/Partomatic")
90
+ """
91
+ self._config.load_config(configuration, **kwargs)
92
+
93
+ def __init__(self, configuration: any = None, **kwargs):
94
+ """
95
+ loads a partomatic configuration from a file or valid yaml
96
+ -------
97
+ arguments:
98
+ - configuration: the path to the configuration file
99
+ OR
100
+ a valid yaml configuration string
101
+ OR
102
+ None (default) for an empty object
103
+ - **kwargs: specific fields to set in the configuration
104
+ -------
105
+ notes:
106
+ you can assign yaml_tree as a kwarg here to load a
107
+ configuration from a node node deep within the yaml tree,
108
+ following the node names separated by slashes
109
+ (example: "BigObject/Partomatic")
110
+ """
111
+ self.parts = []
112
+ self._config = self.__class__._config
113
+ self.load_config(configuration, **kwargs)
114
+
115
+ def partomate(self):
116
+ """automates the part generation and exports stl and step models
117
+ -------
118
+ notes:
119
+ - if you want to avoid exporting one of those file formats,
120
+ you can override the export_stls or export_steps methods
121
+ with a no-op method using the pass keyword
122
+ """
123
+ self.compile()
124
+ self.export_stls()
@@ -0,0 +1,131 @@
1
+ """Part extended for CI/CD automation"""
2
+
3
+ from dataclasses import dataclass, field, fields, is_dataclass, MISSING
4
+ from enum import Enum, Flag
5
+ from pathlib import Path
6
+
7
+ import yaml
8
+
9
+
10
+ class AutoDataclassMeta(type):
11
+ def __new__(cls, name, bases, dct):
12
+ new_cls = super().__new__(cls, name, bases, dct)
13
+ return dataclass(init=False)(new_cls)
14
+
15
+
16
+ @dataclass
17
+ class PartomaticConfig(metaclass=AutoDataclassMeta):
18
+ yaml_tree: str = "Part"
19
+ stl_folder: str = "NONE"
20
+ file_prefix: str = ""
21
+ file_suffix: str = ""
22
+ create_folders_if_missing: bool = True
23
+
24
+ def _default_config(self):
25
+ """
26
+ Resets all values to their default values.
27
+ """
28
+ for field in fields(self):
29
+ if field.default is not MISSING:
30
+ setattr(self, field.name, field.default)
31
+ elif field.default_factory is not MISSING:
32
+ setattr(self, field.name, field.default_factory())
33
+ else:
34
+ raise ValueError(f"Field {field.name} has no default value")
35
+
36
+ def load_config(self, configuration: any, **kwargs):
37
+ """
38
+ loads a partomatic configuration from a file or valid yaml
39
+ -------
40
+ arguments:
41
+ - configuration: the path to the configuration file
42
+ OR
43
+ a valid yaml configuration string
44
+ -------
45
+ notes:
46
+ if yaml_tree is set in the PartomaticConfig descendent,
47
+ PartomaticConfig will use that tree to find a node deep
48
+ within the yaml tree, following the node names separated by slashes
49
+ (example: "BigObject/Partomatic")
50
+ """
51
+ if "yaml_tree" in kwargs:
52
+ self.yaml_tree = kwargs["yaml_tree"]
53
+ if isinstance(configuration, self.__class__):
54
+ for field in fields(self):
55
+ setattr(self, field.name, getattr(configuration, field.name))
56
+ return
57
+ if configuration is not None:
58
+ configuration = str(configuration)
59
+ if "\n" not in configuration:
60
+ path = Path(configuration)
61
+ if path.exists() and path.is_file():
62
+ configuration = path.read_text()
63
+ bracket_dict = yaml.safe_load(configuration)
64
+ for node in self.yaml_tree.split("/"):
65
+ if node not in bracket_dict:
66
+ raise ValueError(
67
+ f"Node {node} not found in configuration file"
68
+ )
69
+ bracket_dict = bracket_dict[node]
70
+
71
+ for classfield in fields(self.__class__):
72
+ if classfield.name in bracket_dict:
73
+ value = bracket_dict[classfield.name]
74
+ if isinstance(classfield.type, type) and issubclass(
75
+ classfield.type, (Enum, Flag)
76
+ ):
77
+ setattr(
78
+ self,
79
+ classfield.name,
80
+ classfield.type[value.upper()],
81
+ )
82
+ elif is_dataclass(classfield.type) and isinstance(
83
+ value, dict
84
+ ):
85
+ setattr(
86
+ self,
87
+ classfield.name,
88
+ classfield.type(**value),
89
+ )
90
+ else:
91
+ setattr(self, classfield.name, value)
92
+
93
+ def __init__(self, configuration: any = None, **kwargs):
94
+ """
95
+ loads a partomatic configuration from a file or valid yaml
96
+ -------
97
+ arguments:
98
+ - configuration: the path to the configuration file
99
+ OR
100
+ a valid yaml configuration string
101
+ OR
102
+ None (default) for an empty object
103
+ - **kwargs: specific fields to set in the configuration
104
+ -------
105
+ notes:
106
+ you can assign yaml_tree as a kwarg here to load a
107
+ configuration from a node node deep within the yaml tree,
108
+ following the node names separated by slashes
109
+ (example: "BigObject/Partomatic")
110
+ """
111
+ if "yaml_tree" in kwargs:
112
+ self.yaml_tree = kwargs["yaml_tree"]
113
+ if configuration is not None:
114
+ self.load_config(configuration, yaml_tree=self.yaml_tree)
115
+ elif kwargs:
116
+ self._default_config()
117
+ for key, value in kwargs.items():
118
+ classfield = next(
119
+ (f for f in fields(self.__class__) if f.name == key),
120
+ None,
121
+ )
122
+ if classfield:
123
+ if is_dataclass(classfield.type):
124
+ if isinstance(value, dict):
125
+ setattr(self, key, classfield.type(**value))
126
+ else:
127
+ setattr(self, key, value)
128
+ else:
129
+ setattr(self, key, value)
130
+ else:
131
+ self._default_config()
@@ -0,0 +1,32 @@
1
+ Metadata-Version: 2.4
2
+ Name: partomatic
3
+ Version: 0.0.1
4
+ Summary: build123d Part extended for CI/CD automation
5
+ Project-URL: Homepage, https://github.com/x0pher/partomatic
6
+ Project-URL: Issues, https://github.com/x0pher/partomatic/issues
7
+ Project-URL: docs, https://partomatic.readthedocs.org
8
+ Project-URL: documentation, https://partomatic.readthedocs.org
9
+ Author: x0pherl
10
+ License-File: LICENSE
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Programming Language :: Python :: 3
14
+ Requires-Python: >=3.8
15
+ Description-Content-Type: text/markdown
16
+
17
+ # Partomatic
18
+
19
+ Partomatic is an attempt to build an automatable ecosystem for generating parametric models through automation -- making CI/CD automation possible for your 3d models.
20
+
21
+ # The Partomatic philosophy
22
+
23
+ Build123d is a powerful library, but it leaves the creation of final parts up to the developer. For a large project with many related and interlocking parts, this can make releasing a new version a project in and of itself.
24
+
25
+ [Partomatic](https://github.com/x0pherl/partomatic) enables _parametric modeling_ and standardizes some _build automation_ for a part.
26
+
27
+ ## Parametric Modeling
28
+ Parametric 3D modeling is a method of creating 3D models where the geometry is defined by parameters, allowing for easy adjustment by simply changing the values of these parameters. This approach enables the creation of flexible and reusable designs that can be quickly adapted to different requirements.
29
+
30
+ ## Build Automation
31
+
32
+ Build automation is a common practice in the software delivery world. Continuous Integration uses build automation to deliver software into testing and production environments whenever changes are checked in by a developer. Partomatic wraps additional information about how to name files and where to store them, so that an automated build script generate and save those parts.
@@ -0,0 +1,8 @@
1
+ partomatic/__init__.py,sha256=KyBzMae6m-DNdr5PDTlEImmPSrX2i9-arys6s2ob77o,128
2
+ partomatic/buildable_part.py,sha256=m1S-JuEjIzBOK1M75FdpMFWekwOVmBmT44wv0LN0cFU,1299
3
+ partomatic/partomatic.py,sha256=3mfyGah8XFpw_ElrFQNao_XhiOkRZHhLloqwM6ZbOqA,4311
4
+ partomatic/partomatic_config.py,sha256=aGlLesFIRyvS_BNqpSW2ZYvrAE1oArUEUGY4NngkzIU,5168
5
+ partomatic-0.0.1.dist-info/METADATA,sha256=_q93pjtfVbuDZOc4Qj3-ycF4vB73Vj8VfgQyh1j6EAs,1898
6
+ partomatic-0.0.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
7
+ partomatic-0.0.1.dist-info/licenses/LICENSE,sha256=9PaWMWFAAGGaGsLa_BpN-Vd89Xca8eNlg3dbOExalCk,1074
8
+ partomatic-0.0.1.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.27.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,7 @@
1
+ Copyright 2024 Christopher Litsinger
2
+
3
+ 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:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ 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.