Skip to content

Commit

Permalink
Black formatting pass
Browse files Browse the repository at this point in the history
  • Loading branch information
jmwright committed Oct 23, 2023
1 parent 9acc09c commit 109f14f
Show file tree
Hide file tree
Showing 20 changed files with 321 additions and 109 deletions.
146 changes: 102 additions & 44 deletions src/cq_cli/cq_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@
import json
from cqcodecs import loader


def build_and_parse(script_str, params, errfile):
"""
Uses CQGI to parse and build a script, substituting in parameters if any were supplied.
"""
# We need to do a broad try/catch to let the user know if something higher-level fails
# We need to do a broad try/catch to let the user know if something higher-level fails
try:
# Do the CQGI handling of the script here and, if successful, pass the build result to the codec
cqModel = cqgi.parse(script_str)
Expand All @@ -22,15 +23,15 @@ def build_and_parse(script_str, params, errfile):
# Handle the case of the build not being successful, otherwise pass the codec the build result
if not build_result.success:
# Re-throw the exception so that it will be caught and formatted correctly
raise(build_result.exception)
raise (build_result.exception)
else:
return build_result
except Exception:
out_tb = traceback.format_exc()

# If there was an error file specified write to that, otherwise send it to stderr
if errfile != None:
with open(errfile, 'w') as file:
with open(errfile, "w") as file:
file.write(str(out_tb))
else:
print(str(out_tb), file=sys.stderr)
Expand All @@ -41,6 +42,7 @@ def build_and_parse(script_str, params, errfile):
# Return None here to prevent a failed build from slipping through
return None


def get_script_from_infile(infile, outfile, errfile):
"""
Gets the CadQuery script from the infile location.
Expand All @@ -58,7 +60,7 @@ def get_script_from_infile(infile, outfile, errfile):
if errfile == None:
print("infile does not exist.", file=sys.stderr)
else:
with open(errfile, 'w') as file:
with open(errfile, "w") as file:
file.write("Argument error: infile does not exist.")

return None
Expand All @@ -68,7 +70,7 @@ def get_script_from_infile(infile, outfile, errfile):
# Grab the string from stdin
script_str = sys.stdin.read()
else:
with open(infile, 'r') as file:
with open(infile, "r") as file:
script_str = file.read()

return script_str
Expand Down Expand Up @@ -96,7 +98,7 @@ def get_params_from_file(param_json_path, errfile):
# Make sure that the file exists
if os.path.isfile(param_json_path):
# Read the contents of the file
with open(param_json_path, 'r') as file:
with open(param_json_path, "r") as file:
params_json = file.read()
param_dict_array = json.loads(params_json)

Expand All @@ -112,10 +114,15 @@ def get_params_from_file(param_json_path, errfile):
param_dict[key] = param_dict_array[key]
else:
if errfile == None:
print("Parameter file does not exist, default parameters will be used. ", file=sys.stderr)
print(
"Parameter file does not exist, default parameters will be used. ",
file=sys.stderr,
)
else:
with open(errfile, 'w') as file:
file.write("Argument error: Parameter file does not exist, default parameters will be used.")
with open(errfile, "w") as file:
file.write(
"Argument error: Parameter file does not exist, default parameters will be used."
)

return param_dict

Expand All @@ -131,21 +138,52 @@ def main():
loaded_codecs = loader.load_codecs()

# Parse the command line arguments
parser = argparse.ArgumentParser(description='Command line utility for converting CadQuery script output to various other output formats.')
parser.add_argument('--codec', help='The codec to use when converting the CadQuery output. Must match the name of a codec file in the cqcodecs directory.')
parser.add_argument('--getparams', help='Analyzes the script and returns a JSON string with the parameter information.')
parser.add_argument('--infile', help='The input CadQuery script to convert.')
parser.add_argument('--outfile', help='File to write the converted CadQuery output to. Prints to stdout if not specified.')
parser.add_argument('--errfile', help='File to write any errors to. Prints to stderr if not specified.')
parser.add_argument('--params', help='A colon and semicolon delimited string (no spaces) of key/value pairs representing variables and their values in the CadQuery script. i.e. var1:10.0;var2:4.0;')
parser.add_argument('--outputopts', dest='opts', help='A colon and semicolon delimited string (no spaces) of key/value pairs representing options to pass to the selected codec. i.e. width:100;height:200;')
parser.add_argument('--validate', help='Setting to true forces the CLI to only parse and validate the script and not produce converted output.')
parser = argparse.ArgumentParser(
description="Command line utility for converting CadQuery script output to various other output formats."
)
parser.add_argument(
"--codec",
help="The codec to use when converting the CadQuery output. Must match the name of a codec file in the cqcodecs directory.",
)
parser.add_argument(
"--getparams",
help="Analyzes the script and returns a JSON string with the parameter information.",
)
parser.add_argument("--infile", help="The input CadQuery script to convert.")
parser.add_argument(
"--outfile",
help="File to write the converted CadQuery output to. Prints to stdout if not specified.",
)
parser.add_argument(
"--errfile",
help="File to write any errors to. Prints to stderr if not specified.",
)
parser.add_argument(
"--params",
help="A colon and semicolon delimited string (no spaces) of key/value pairs representing variables and their values in the CadQuery script. i.e. var1:10.0;var2:4.0;",
)
parser.add_argument(
"--outputopts",
dest="opts",
help="A colon and semicolon delimited string (no spaces) of key/value pairs representing options to pass to the selected codec. i.e. width:100;height:200;",
)
parser.add_argument(
"--validate",
help="Setting to true forces the CLI to only parse and validate the script and not produce converted output.",
)

args = parser.parse_args()

# Make sure that the user has at least specified the validate or codec arguments
if args.validate == None and args.infile == None and args.codec == None and args.outfile == None:
print("Please specify at least the validate option plus an infile, or an infile and an outfile or a codec.")
if (
args.validate == None
and args.infile == None
and args.codec == None
and args.outfile == None
):
print(
"Please specify at least the validate option plus an infile, or an infile and an outfile or a codec."
)
parser.print_help(sys.stderr)
sys.exit(2)

Expand All @@ -167,9 +205,10 @@ def main():
# Validation handling
#
# If the user wants to validate, do that and exit
if args.validate == 'true':
if args.validate == "true":
script_str = get_script_from_infile(args.infile, outfile, errfile)
if script_str == None: sys.exit(1)
if script_str == None:
sys.exit(1)

# Set the PYTHONPATH variable to the current directory to allow module loading
set_pythonpath_for_infile(args.infile)
Expand All @@ -180,10 +219,10 @@ def main():
if build_result != None and build_result.success:
# Let the user know that the validation was a success
if outfile != None:
with open(outfile, 'w') as file:
file.write('validation_success')
with open(outfile, "w") as file:
file.write("validation_success")
else:
print('validation_success')
print("validation_success")

return 0

Expand All @@ -198,7 +237,8 @@ def main():

# Load the script string
script_str = get_script_from_infile(args.infile, outfile, errfile)
if script_str == None: sys.exit(1)
if script_str == None:
sys.exit(1)

# Set the PYTHONPATH variable to the current directory to allow module loading
set_pythonpath_for_infile(args.infile)
Expand Down Expand Up @@ -249,10 +289,10 @@ def main():
params.append(new_dict)

# Write the converted output to the appropriate place based on the command line arguments
if args.getparams == 'true':
if args.getparams == "true":
print(json.dumps(params))
else:
with open(args.getparams, 'w') as file:
with open(args.getparams, "w") as file:
file.write(json.dumps(params))

# Check to see if the user only cared about getting the params
Expand All @@ -268,24 +308,26 @@ def main():
# Attempt to auto-detect the codec if the user has not set the option
if args.outfile != None and args.codec == None:
# Determine the codec from the file extension
codec_temp = args.outfile.split('.')[-1]
codec_temp = args.outfile.split(".")[-1]
if codec_temp != None:
codec_temp = "cq_codec_" + codec_temp
if codec_temp in loaded_codecs:
codec = codec_temp

# If the user has not supplied a codec, they need to be validating the script
if (codec == None and args.outfile == None) and (args.validate == None or args.validate == 'false'):
if (codec == None and args.outfile == None) and (
args.validate == None or args.validate == "false"
):
print("Please specify a valid codec. You have the following to choose from:")
for key in loaded_codecs:
print(key.replace('cq_codec_', ''))
print(key.replace("cq_codec_", ""))
sys.exit(3)

# If the codec is None at this point, the user specified an invalid codec
if codec == None:
print("Please specify a valid codec. You have the following to choose from:")
for key in loaded_codecs:
print(key.replace('cq_codec_', ''))
print(key.replace("cq_codec_", ""))
sys.exit(3)

for key in loaded_codecs:
Expand All @@ -300,7 +342,8 @@ def main():

# Grab the script input from a file path or stdin
script_str = get_script_from_infile(infile, outfile, errfile)
if script_str == None: sys.exit(1)
if script_str == None:
sys.exit(1)

# Set the PYTHONPATH variable to the current directory to allow module loading
set_pythonpath_for_infile(args.infile)
Expand All @@ -311,7 +354,13 @@ def main():
# Check whether any parameters were passed
if args.params != None:
# We have been passed a directory
if args.params.startswith('/') or args.params.startswith('.') or args.params.startswith('..') or args.params.startswith('~') or args.params[1] == ':':
if (
args.params.startswith("/")
or args.params.startswith(".")
or args.params.startswith("..")
or args.params.startswith("~")
or args.params[1] == ":"
):
# Load the parameters dictionary from the file
file_params = get_params_from_file(args.params, errfile)

Expand All @@ -323,9 +372,9 @@ def main():
params = json.loads(args.params)
else:
# Convert the string of parameters into a params dictionary
groups = args.params.split(';')
groups = args.params.split(";")
for group in groups:
param_parts = group.split(':')
param_parts = group.split(":")
# Protect against a trailing semi-colon
if len(param_parts) == 2:
params[param_parts[0]] = param_parts[1]
Expand All @@ -336,9 +385,9 @@ def main():
# Check whether any output options were passed
if args.opts != None:
# Convert the string of options into a output_opts dictionary
groups = args.opts.split(';')
groups = args.opts.split(";")
for group in groups:
opt_parts = group.split(':')
opt_parts = group.split(":")
# Protect against a trailing semi-colon
if len(opt_parts) == 2:
op1 = opt_parts[1]
Expand All @@ -347,7 +396,12 @@ def main():
if op1 == "True" or op1 == "False":
op = opt_parts[1] == "True"
elif op1[:1] == "(":
op = tuple(map(float, opt_parts[1].replace("(", "").replace(")", "").split(',')))
op = tuple(
map(
float,
opt_parts[1].replace("(", "").replace(")", "").split(","),
)
)
elif "." in op1:
op = float(opt_parts[1])
else:
Expand All @@ -370,7 +424,7 @@ def main():
if errfile == None:
print("build_and_parse error: " + str(err), file=sys.stderr)
else:
with open(errfile, 'w') as file:
with open(errfile, "w") as file:
file.write(err)
sys.exit(100)

Expand All @@ -389,13 +443,16 @@ def main():
print(converted)
else:
if isinstance(converted, str):
with open(outfile, 'w') as file:
with open(outfile, "w") as file:
file.write(converted)
elif isinstance(converted, (bytes, bytearray)):
with open(outfile, 'wb') as file:
with open(outfile, "wb") as file:
file.write(converted)
else:
raise TypeError("Expected converted output to be str, bytes, or bytearray. Got '%s'" % type(converted).__name__)
raise TypeError(
"Expected converted output to be str, bytes, or bytearray. Got '%s'"
% type(converted).__name__
)

except Exception:
out_tb = traceback.format_exc()
Expand All @@ -404,10 +461,11 @@ def main():
if errfile == None:
print("Conversion codec error: " + str(out_tb), file=sys.stderr)
else:
with open(errfile, 'w') as file:
with open(errfile, "w") as file:
file.write(str(out_tb))

sys.exit(200)


if __name__ == "__main__":
main()
7 changes: 4 additions & 3 deletions src/cq_cli/cqcodecs/codec_helpers.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
from contextlib import contextmanager,redirect_stderr,redirect_stdout
from contextlib import contextmanager, redirect_stderr, redirect_stdout
from os import devnull


@contextmanager
def suppress_stdout_stderr():
"""A context manager that redirects stdout and stderr to devnull"""
with open(devnull, 'w') as fnull:
with open(devnull, "w") as fnull:
with redirect_stderr(fnull) as err, redirect_stdout(fnull) as out:
yield (err, out)
yield (err, out)
9 changes: 6 additions & 3 deletions src/cq_cli/cqcodecs/cq_codec_gltf.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import cadquery as cq
import cqcodecs.codec_helpers as helpers


def convert(build_result, output_file=None, error_file=None, output_opts=None):
# Create a temporary file to put the STL output into
temp_dir = tempfile.gettempdir()
Expand All @@ -15,10 +16,12 @@ def convert(build_result, output_file=None, error_file=None, output_opts=None):
if type(build_result.first_result.shape).__name__ == "Assembly":
build_result.first_result.shape.save(temp_file, binary=False)
else:
raise ValueError("GLTF export is only available for CadQuery assemblies at this time")
raise ValueError(
"GLTF export is only available for CadQuery assemblies at this time"
)

# Read the GLTF output back in
with open(temp_file, 'r') as file:
with open(temp_file, "r") as file:
gltf_str = file.read()

return gltf_str
return gltf_str
7 changes: 5 additions & 2 deletions src/cq_cli/cqcodecs/cq_codec_step.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import cadquery as cq
import cqcodecs.codec_helpers as helpers


def convert(build_result, output_file=None, error_file=None, output_opts=None):
# Create a temporary file to put the STL output into
temp_dir = tempfile.gettempdir()
Expand All @@ -11,10 +12,12 @@ def convert(build_result, output_file=None, error_file=None, output_opts=None):
# The exporters will add extra output that we do not want, so suppress it
with helpers.suppress_stdout_stderr():
# Put the STEP output into the temp file
exporters.export(build_result.results[0].shape, temp_file, exporters.ExportTypes.STEP)
exporters.export(
build_result.results[0].shape, temp_file, exporters.ExportTypes.STEP
)

# Read the STEP output back in
with open(temp_file, 'r') as file:
with open(temp_file, "r") as file:
step_str = file.read()

return step_str
Loading

0 comments on commit 109f14f

Please sign in to comment.