symforce-org / symforce

Fast symbolic computation, code generation, and nonlinear optimization for robotics
https://symforce.org
Apache License 2.0
1.41k stars 145 forks source link

TemplateList.render supports specifying external search paths #187

Closed gareth-cross closed 1 year ago

gareth-cross commented 2 years ago

This change adds a search_paths argument such that external search paths can be specified when calling TemplateList.render. This allows the user to specify external directories to search when jinja resolves import/include statements in the template.

The use-case for this is to allow templates to span multiple directories - for example, some of them residing in the symforce repo and others in the user's own code.

aaron-skydio commented 2 years ago

Lint failure (we'll have these visible on PRs as well once #189 merges):

symforce/codegen/template_util.py:183: error: Argument "search_paths" to "__call__" of "_lru_cache_wrapper" has incompatible type "Iterable[Union[str, PathLike[Any]]]"; expected "Hashable"  [arg-type]
aaron-skydio commented 2 years ago

Also autoformat:

20:58:47  --- symforce/codegen/template_util.py 2022-07-12 03:49:47.276795 +0000
20:58:47  +++ symforce/codegen/template_util.py 2022-07-12 03:58:42.441861 +0000
20:58:47  @@ -128,11 +128,13 @@
20:58:47           + source
20:58:47       )
20:58:47   
20:58:47   
20:58:47   @functools.lru_cache
20:58:47  -def jinja_env(template_dir: T.Openable, search_paths: T.Iterable[T.Openable] = ()) -> RelEnvironment:
20:58:47  +def jinja_env(
20:58:47  +    template_dir: T.Openable, search_paths: T.Iterable[T.Openable] = ()
20:58:47  +) -> RelEnvironment:
20:58:47       """
20:58:47       Helper function to cache the Jinja environment, which enables caching of loaded templates
20:58:47       """
20:58:47       all_search_paths = [os.fspath(template_dir)]
20:58:47       all_search_paths.extend(search_paths)
20:58:47  @@ -151,11 +153,11 @@
20:58:47       template_path: T.Openable,
20:58:47       data: T.Dict[str, T.Any],
20:58:47       output_path: T.Optional[T.Openable] = None,
20:58:47       template_dir: T.Openable = CURRENT_DIR,
20:58:47       autoformat: bool = True,
20:58:47  -    search_paths: T.Iterable[T.Openable] = ()
20:58:47  +    search_paths: T.Iterable[T.Openable] = (),
20:58:47   ) -> str:
20:58:47       """
20:58:47       Boilerplate to render template. Returns the rendered string and optionally writes to file.
20:58:47   
20:58:47       Args:
20:58:47  @@ -178,11 +180,13 @@
20:58:47   
20:58:47       template_name = template_path.resolve().relative_to(template_dir.resolve())
20:58:47   
20:58:47       filetype = FileType.from_template_path(Path(template_name))
20:58:47   
20:58:47  -    template = jinja_env(template_dir, search_paths=search_paths).get_template(os.fspath(template_name))
20:58:47  +    template = jinja_env(template_dir, search_paths=search_paths).get_template(
20:58:47  +        os.fspath(template_name)
20:58:47  +    )
20:58:47       rendered_str = add_preamble(
20:58:47           str(template.render(**data)), template_name, comment_prefix=filetype.comment_prefix()
20:58:47       )
20:58:47   
20:58:47       if autoformat:
20:58:47  @@ -219,12 +223,16 @@
20:58:47       ) -> None:
20:58:47           self.items.append(
20:58:47               self.TemplateListEntry(template_path=template_path, output_path=output_path, data=data)
20:58:47           )
20:58:47   
20:58:47  -    def render(self, autoformat: bool = True, template_dir: T.Openable = CURRENT_DIR,
20:58:47  -               search_paths: T.Iterable[T.Openable] = ()) -> None:
20:58:47  +    def render(
20:58:47  +        self,
20:58:47  +        autoformat: bool = True,
20:58:47  +        template_dir: T.Openable = CURRENT_DIR,
20:58:47  +        search_paths: T.Iterable[T.Openable] = (),
20:58:47  +    ) -> None:
20:58:47           for entry in self.items:
20:58:47               render_template(
20:58:47                   template_path=entry.template_path,
20:58:47                   output_path=entry.output_path,
20:58:47                   data=entry.data,
20:58:47  would reformat symforce/codegen/template_util.py
20:58:47  Oh no! 💥 💔 💥
20:58:47  1 file would be reformatted, 131 files would be left unchanged.