Skip to content

Commit

Permalink
Render packages recursively (#175)
Browse files Browse the repository at this point in the history
  • Loading branch information
openvmp authored Aug 19, 2024
1 parent 3ec004c commit 20e59c3
Show file tree
Hide file tree
Showing 5 changed files with 190 additions and 60 deletions.
90 changes: 55 additions & 35 deletions partcad-cli/src/partcad_cli/cli_render.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ def cli_help_render(subparsers: argparse.ArgumentParser):
dest="package",
default="",
)
parser_render.add_argument(
"-r",
help="Recursively render all imported packages",
dest="recursive",
action="store_true",
)

group_type = parser_render.add_mutually_exclusive_group(required=False)
group_type.add_argument(
Expand Down Expand Up @@ -96,43 +102,57 @@ def cli_help_render(subparsers: argparse.ArgumentParser):
def cli_render(args, ctx):
ctx.option_create_dirs = args.create_dirs

if args.package is None:
args.package = ""
if not args.object is None:
if not ":" in args.object:
args.object = ":" + args.object
args.package, args.object = pc_utils.resolve_resource_path(
ctx.get_current_project_path(), args.object
package = args.package if args.package is not None else ""
if args.recursive:
start_package = pc_utils.get_child_project_path(
ctx.get_current_project_path(), package
)

if args.object is None:
# Render all parts and assemblies configured to be auto-rendered in this project
ctx.render(
project_path=args.package,
format=args.format,
output_dir=args.output_dir,
all_packages = ctx.get_all_packages(start_package)
packages = list(
map(
lambda p: p["name"],
list(all_packages),
)
)
else:
# Render the requested part or assembly
sketches = []
interfaces = []
parts = []
assemblies = []
if args.sketch:
sketches.append(args.object)
elif args.interface:
interfaces.append(args.object)
elif args.assembly:
assemblies.append(args.object)
packages = [package]

for package in packages:
if not args.object is None:
if not ":" in args.object:
args.object = ":" + args.object
args.package, args.object = pc_utils.resolve_resource_path(
ctx.get_current_project_path(), args.object
)

if args.object is None:
# Render all parts and assemblies configured to be auto-rendered in this project
ctx.render(
project_path=package,
format=args.format,
output_dir=args.output_dir,
)
else:
parts.append(args.object)
# Render the requested part or assembly
sketches = []
interfaces = []
parts = []
assemblies = []
if args.sketch:
sketches.append(args.object)
elif args.interface:
interfaces.append(args.object)
elif args.assembly:
assemblies.append(args.object)
else:
parts.append(args.object)

prj = ctx.get_project(args.package)
prj.render(
sketches=sketches,
interfaces=interfaces,
parts=parts,
assemblies=assemblies,
format=args.format,
output_dir=args.output_dir,
)
prj = ctx.get_project(package)
prj.render(
sketches=sketches,
interfaces=interfaces,
parts=parts,
assemblies=assemblies,
format=args.format,
output_dir=args.output_dir,
)
60 changes: 42 additions & 18 deletions partcad/src/partcad/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -241,10 +241,13 @@ def get_project(self, rel_project_path: str):
# assume that the root package has an 'onlyInRoot' dependency
# present to facilitate such a reference in a standalone
# development environment.
project_path = self.name + project_path

# Strip the first '/' (absolute path always starts with a '/'``)
len_to_skip = len(self.name) + 1 if self.name != "/" else 1
if self.name != "/" and project_path.startswith(self.name):
# The root package is not '/, need to skip the root package name
len_to_skip = len(self.name) + 1
else:
len_to_skip = 1
project_path = project_path[len_to_skip:]

project = self.projects[self.name]
Expand Down Expand Up @@ -292,7 +295,7 @@ def _get_project_recursive(self, project, import_list: list[str]):
)
):
pc_logging.debug(
"Importing a subfolder: %s..." % next_project_path
"Importing a subfolder (get): %s..." % next_project_path
)
prj_conf = {
"name": next_project_path,
Expand Down Expand Up @@ -320,11 +323,16 @@ def _get_project_recursive(self, project, import_list: list[str]):
)
imports = list(filtered)
for prj_name in imports:
pc_logging.debug("Checking the import: %s..." % prj_name)
pc_logging.debug(
"Checking the import: %s vs %s..."
% (prj_name, next_import)
)
if prj_name != next_import:
continue
pc_logging.debug("Importing: %s..." % next_project_path)
prj_conf = project.config_obj["import"][prj_name]
if prj_conf.get("onlyInRoot", False):
next_project_path = "/" + prj_name
pc_logging.debug("Importing: %s..." % next_project_path)
if "name" in prj_conf:
prj_conf["orig_name"] = prj_conf["name"]
prj_conf["name"] = next_project_path
Expand All @@ -338,8 +346,10 @@ def _get_project_recursive(self, project, import_list: list[str]):

return next_project

def import_all(self):
asyncio.run(self._import_all_wrapper(self.projects[self.name]))
def import_all(self, parent_name=None):
if parent_name is None:
parent_name = self.name
asyncio.run(self._import_all_wrapper(self.projects[parent_name]))

async def _import_all_wrapper(self, project):
iterate_tasks = []
Expand Down Expand Up @@ -394,13 +404,18 @@ async def _import_all_recursive(self, project):
imports,
)
imports = list(filtered)

for prj_name in imports:
next_project_path = get_child_project_path(
project.name, prj_name
)
prj_conf = project.config_obj["import"][prj_name]

if prj_conf.get("onlyInRoot", False):
next_project_path = "/" + prj_name
else:
next_project_path = get_child_project_path(
project.name, prj_name
)
pc_logging.debug("Importing: %s..." % next_project_path)
prj_conf = project.config_obj["import"][prj_name]

if "name" in prj_conf:
prj_conf["orig_name"] = prj_conf["name"]
prj_conf["name"] = next_project_path
Expand All @@ -423,9 +438,11 @@ async def _import_all_recursive(self, project):
consts.DEFAULT_PACKAGE_CONFIG,
)
):
# TODO(clairbee): check if this subdir is already imported
next_project_path = get_child_project_path(project.name, subdir)
pc_logging.debug(
"Importing a subfolder: %s..." % next_project_path
"Importing a subfolder (import all): %s..."
% next_project_path
)
prj_conf = {
"name": next_project_path,
Expand All @@ -441,18 +458,25 @@ async def _import_all_recursive(self, project):

return tasks

def get_all_packages(self):
def get_all_packages(self, parent_name=None):
# TODO(clairbee): leverage root_project.get_child_project_names()
self.import_all()
return self.get_packages()

def get_packages(self):
self.import_all(parent_name)
return self.get_packages(parent_name)

def get_packages(self, parent_name=None):
projects = self.projects.values()
if parent_name is not None:
projects = filter(
lambda x: x.name.startswith(parent_name), projects
)
return map(
lambda pkg: {"name": pkg.name, "desc": pkg.desc},
filter(
# FIXME(clairbee): parameterize interfaces before displaying them
# lambda x: len(x.interfaces) + len(x.sketches) + len(x.parts) + len(x.assemblies)
lambda x: len(x.sketches) + len(x.parts) + len(x.assemblies)
> 0,
self.projects.values(),
projects,
),
)

Expand Down
87 changes: 83 additions & 4 deletions partcad/src/partcad/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -1223,7 +1223,7 @@ async def render_async(
# Render
tasks = []
for shape in shapes:
shape_render = render
shape_render = copy.copy(render)
if (
"render" in shape.config
and not shape.config["render"] is None
Expand Down Expand Up @@ -1406,6 +1406,8 @@ def render_readme_async(self, render_cfg, output_dir):
cfg = {}

path = os.path.join(output_dir, cfg.get("path", "README.md"))
dir_path = os.path.dirname(path)
return_path = os.path.relpath(output_dir, dir_path)

exclude = cfg.get("exclude", [])
if exclude is None:
Expand Down Expand Up @@ -1436,19 +1438,86 @@ def render_readme_async(self, render_cfg, output_dir):
lines += [usage]
lines += [""]

if (
self.config_obj.get("import", None) is not None
and not "packages" in exclude
):
imports = self.config_obj["import"]
display_imports = []
for alias in imports:
if imports[alias].get("onlyInRoot", False):
continue
display_imports.append(alias)

if display_imports:
lines += ["## Sub-Packages"]
lines += [""]
for alias in display_imports:
import_config = imports[alias]
columns = []

if (
"type" not in import_config
or import_config["type"] == "local"
):
lines += [
"### [%s](./%s)"
% (
import_config["name"],
os.path.join(
return_path,
import_config["path"],
"README.md",
),
)
]
elif import_config["type"] == "git":
lines += [
"### [%s](%s)"
% (import_config["name"], import_config["url"])
]
else:
lines += ["### %s" % import_config["name"]]

if "desc" in import_config:
columns += [import_config["desc"]]
elif not columns:
columns += ["***Not documented yet.***"]

if len(columns) > 1:
lines += ["<table><tr>"]
lines += map(
lambda c: "<td valign=top>" + c + "</td>", columns
)
lines += ["</tr></table>"]
else:
lines += columns
lines += [""]

def add_section(name, shape, render_cfg):
config = shape.config

if "type" in config and config["type"] == "alias":
if (
"type" in config
and config["type"] == "alias"
and "aliases" in exclude
):
return []

columns = []
if "svg" in render_cfg or (
"type" in config and config["type"] == "svg"
):
svg_cfg = render_cfg["svg"] if "svg" in render_cfg else {}
if isinstance(svg_cfg, str):
svg_cfg = {"prefix": svg_cfg}
svg_cfg = svg_cfg if svg_cfg is not None else {}
image_path = os.path.join(
return_path,
svg_cfg.get("prefix", "."),
name + ".svg",
)
test_image_path = os.path.join(
svg_cfg.get("prefix", "."),
name + ".svg",
)
Expand All @@ -1459,16 +1528,24 @@ def add_section(name, shape, render_cfg):
png_cfg = render_cfg["png"]
png_cfg = png_cfg if png_cfg is not None else {}
image_path = os.path.join(
return_path,
png_cfg.get("prefix", "."),
name + ".png",
)
test_image_path = os.path.join(
png_cfg.get("prefix", "."),
name + ".png",
)
columns += ['<img src="%s" height="200">' % image_path]
else:
image_path = None

if image_path is None or not os.path.exists(image_path):
if image_path is None or not os.path.exists(
os.path.join(output_dir, test_image_path)
):
pc_logging.warn(
"Skipping rendering of %s: no image found" % name
"Skipping rendering of %s: no image found at %s"
% (name, test_image_path)
)
return []

Expand Down Expand Up @@ -1540,6 +1617,8 @@ def add_section(name, shape, render_cfg):
lines += add_section(name, shape, render_cfg)

lines += [
"<br/><br/>",
"",
"*Generated by [PartCAD](https://partcad.org/)*",
]

Expand Down
Loading

0 comments on commit 20e59c3

Please sign in to comment.