🎉 First commit

This commit is contained in:
Rodolphe Houdas 2021-08-16 23:28:31 +01:00
commit 206c6e1bf0
8 changed files with 440 additions and 0 deletions

149
.gitignore vendored Normal file
View File

@ -0,0 +1,149 @@
# Created by https://www.toptal.com/developers/gitignore/api/python
# Edit at https://www.toptal.com/developers/gitignore?templates=python
### Python ###
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# End of https://www.toptal.com/developers/gitignore/api/python
# VSCode
.vscode

0
README.md Normal file
View File

15
bin/customizer.py Normal file
View File

@ -0,0 +1,15 @@
import argparse
from openscad_pycustomizer import scad_parser
import json
parser = argparse.ArgumentParser(description='Process some integers.')
parser.add_argument('file', metavar='N', type=str,
help='The .scad file')
args = parser.parse_args()
print(json.dumps(
scad_parser.parse(args.file),
cls=scad_parser.CustomizerProfileEncoder,
indent=4
))

77
data/example.scad Normal file
View File

@ -0,0 +1,77 @@
// Random value with no section
Value = 2; // Number
/* [Drop down box:] */
// combo box for number
Numbers=2; // [0, 1, 2, 3]
// combo box for string
Strings="foo"; // [foo, bar, baz]
//labeled combo box for numbers
Labeled_values=10; // [10:L, 20:M, 30:XL]
//labeled combo box for string
Labeled_value="S"; // [S:Small, M:Medium, L:Large]
/*[ Slider ]*/
// slider widget for number
slider =34; // [10:100]
//step slider for number
stepSlider=2; //[0:5:100]
/* [Checkbox] */
//description
Variable = true;
/*[Spinbox] */
// spinbox with step size 1
Spinbox = 5;
/* [Textbox] */
//Text box for vector with more than 4 elements
Vector6=[12,34,44,43,23,23];
// Text box for string
String="hello";
/* [Special vector] */
//Text box for vector with less than or equal to 4 elements
Vector1=[12]; //[0:2:50]
Vector2=[12,34]; //[0:2:50]
Vector3=[12,34,46]; //[0:2:50]
Vector4=[12,34,46,24]; //[0:2:50]
cube([2,3,4]);
echo(version=version());
intersection()
{
linear_extrude(height = 100, center = true, convexity= 3)
import(file = "advance_intersection.dxf");
rotate([0, 90, 0])
linear_extrude(height = 100, center = true, convexity= 3)
import(file = "advance_intersection.dxf");
rotate([90, 0, 0])
linear_extrude(height = 100, center = true, convexity= 3)
import(file = "advance_intersection.dxf");
}
// Written by Clifford Wolf <clifford@clifford.at> and Marius
// Kintel <marius@kintel.net>
//
// To the extent possible under law, the author(s) have dedicated all
// copyright and related and neighboring rights to this software to the
// public domain worldwide. This software is distributed without any
// warranty.
//
// You should have received a copy of the CC0 Public Domain
// Dedication along with this software.
// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.

6
pyproject.toml Normal file
View File

@ -0,0 +1,6 @@
[build-system]
requires = [
"setuptools>=42",
"wheel"
]
build-backend = "setuptools.build_meta"

26
setup.cfg Normal file
View File

@ -0,0 +1,26 @@
[metadata]
name = openscad_pycustomizer
version = 0.0.1
author = Rodolphe Houdas
author_email = rodolphe@lunai.re
description = Parse customizer data from .scad files
long_description = file: README.md
long_description_content_type = text/markdown
url = https://forge.lunai.re/openscad-pycustomizer
project_urls =
Bug Tracker = https://forge.lunai.re/openscad-pycustomizer/issues
classifiers =
Programming Language :: Python :: 3
License :: OSI Approved :: MIT License
Operating System :: OS Independent
[options]
package_dir =
= src
scripts =
bin/customizer.py
packages = find:
python_requires = >=3.6
[options.packages.find]
where = src

View File

View File

@ -0,0 +1,167 @@
import dataclasses
from dataclasses import dataclass
import json
"""
Types that must be supported:
* int
* float
* string
* bool
* list
"""
@dataclass
class Element:
pass
@dataclass
class Variable(Element):
text: str
value: str
description: str
@dataclass
class IntVariable(Variable):
value: int
@dataclass
class FloatVariable(Variable):
value: float
@dataclass
class StringVariable(Variable):
value: str
@dataclass
class BoolVariable(Variable):
value: bool
@dataclass
class Section(Element):
text: str
elements: list[Element]
@dataclass
class CustomizerData:
elements: list[Element]
@staticmethod
def flatten(data):
"""
Return a list of parameters
"""
elems = {}
for elem in data.elements:
if isinstance(elem, Section):
for param in elem.elements:
elems[param.text] = param.value
else:
elems[elem.text] = elem.value
return elems
class EnhancedJSONEncoder(json.JSONEncoder):
def default(self, o):
if dataclasses.is_dataclass(o):
return dataclasses.asdict(o)
return super().default(o)
class CustomizerProfileEncoder(json.JSONEncoder):
def default(self, o):
if isinstance(o, CustomizerData):
return CustomizerData.flatten(o)
return super().default(o)
def parse_type(value):
# Parse type
if ((value[0] == "\"" or value[0] == "'")
and (value[-1] == "\"" or value[-1] == "'")):
value = value.strip("\"").strip("'")
elif value == "true" or value == "false":
value = bool(value)
elif value[0] == "[" and value[-1] == "]":
value = value[1:-2].split(",")
for index in range(len(value)):
value[index] = parse_type(value[index])
elif len(value.split(".")):
value = float(value)
else:
value = int(value)
return value
def parse(file):
with open(file) as scad_file:
section = None
all_elems = []
elems = []
description = None
for line in scad_file.readlines():
line = line.strip()
# Checking for section title
if line[0:2] == "/*":
(middle, end) = line[2:].strip().split("*/")
middle = middle.strip()
if section is None:
all_elems.extend(elems)
else:
all_elems.append(Section(section, elems))
section = middle
elems = []
# Checking for comment
elif line[0:2] == "//":
description = line[2:].strip()
# Checking for text
elif len(line.strip()) > 0 and len(line.split("=")) == 2:
(name, value) = line.split("=")
name = name.strip()
value = value.split(";")[0].strip()
try:
value = parse_type(value)
except ValueError as e:
print(f"Error parsing a value: {e}")
print("Stopping parsing")
break
elems.append(
Variable(
name,
value,
"" if description is None else description
)
)
description = None
elif len(line) == 0:
pass
else:
break
all_elems.append(Section(section, elems))
data = CustomizerData(all_elems)
return data