Skip to content

Commit

Permalink
typehint and intersphinx updates
Browse files Browse the repository at this point in the history
Signed-off-by: Christian López Barrón <[email protected]>
  • Loading branch information
chrizzFTD committed Dec 15, 2024
1 parent 3e519a5 commit 69830da
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 18 deletions.
51 changes: 51 additions & 0 deletions docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
# ones.
extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.extlinks',
'sphinx.ext.doctest',
'sphinx.ext.intersphinx',
'sphinx.ext.inheritance_diagram',
Expand All @@ -47,16 +48,26 @@
'sphinx_inline_tabs',
'hoverxref.extension',
'sphinx.ext.autosectionlabel',
'sphinxcontrib.doxylink',
]

# Offset to play well with copybutton
toggleprompt_offset_right = 35
togglebutton_hint = " "
intersphinx_mapping = {
'python': ('https://docs.python.org/3', None),
'usd': ('https://openusd.org/release/', None),
'networkx': ('https://networkx.org/documentation/stable/', None),
'naming': ('https://naming.readthedocs.io/en/latest/', None),
'grill.names': ('https://grill-names.readthedocs.io/en/latest/', None)
}
# extlinks = {
# 'usd': ('https://openusd.org/release/api/%s.html', '')
# }
extlinks = {
'usdclass': ('https://openusd.org/release/api/class_usd_%s.html', '%s'),
'usdmethod': ('https://openusd.org/release/api/class_usd_%s.html#%s', 'Usd.%s'),
}
hoverxref_auto_ref = True
hoverxref_default_type = 'tooltip'

Expand Down Expand Up @@ -234,3 +245,43 @@

# A list of files that should not be packed into the epub file.
epub_exclude_files = ['search.html']

_USD_TAG_URL = "https://openusd.org/release/USD.tag"
_DOXY_LINK_CACHE_NAME = 'usdcpp'
_DOXY_LINK_ROOT_DIR = "https://openusd.org/release/api/"
doxylink = {
_DOXY_LINK_CACHE_NAME: (_USD_TAG_URL, _DOXY_LINK_ROOT_DIR)
}

def _handle_missing_usd_reference(app, env, node, contnode):
"""Handle missing references by redirecting to a custom URL."""
from docutils import nodes

target = node['reftarget']
if not target.startswith('pxr.'):
return None

pxr_obj_namespace = target.removeprefix('pxr.').replace(".", "")
print("---------")
print(f"{target=}")
print(f"{pxr_obj_namespace=}")
pxr_obj_namespace = {
"UsdInitialLoadSet": "UsdStage::InitialLoadSet", # there's a level of indirection in the python bindings?
"UsdFilter": "UsdPrimCompositionQuery::Filter", # filter is a member of the query type
"UsdCompositionArc": "UsdPrimCompositionQueryArc",
"Usd_Term": "primFlags.h",
"Usd_PrimFlagsConjunction": "primFlags.h",
}.get(pxr_obj_namespace, pxr_obj_namespace)
from sphinxcontrib.doxylink import doxylink
has_explicit_title, title, part = doxylink.split_explicit_title(pxr_obj_namespace)
part = doxylink.utils.unescape(part)
url = app.env.doxylink_cache[_DOXY_LINK_CACHE_NAME]['mapping'][part]
full_url = doxylink.join(_DOXY_LINK_ROOT_DIR, url.file)
print(full_url)
return nodes.reference('', contnode.astext(), refuri=full_url)


def setup(app):
"""Setup Sphinx to handle missing USD references. This can be removed when the USD C++ docs ship with an inventory of the USD types for python bindings."""
app.connect("missing-reference", _handle_missing_usd_reference)
return {"parallel_read_safe": True, "parallel_write_safe": True}
24 changes: 14 additions & 10 deletions grill/cook/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import itertools
import contextlib
import contextvars
import collections
from pathlib import Path
from pprint import pformat

Expand Down Expand Up @@ -93,7 +94,7 @@ def _fetch_layer(identifier: str, context: Ar.ResolverContext) -> Sdf.Layer:
return layer


def asset_identifier(path):
def asset_identifier(path: Path | str):
"""Since identifiers from relative paths can become absolute when opening existing assets, this function ensures to return the value expected to be authored in layers."""
# TODO: temporary public. mmm
# Expect identifiers to not have folders in between.
Expand All @@ -106,7 +107,7 @@ def asset_identifier(path):
return str(path.relative_to(Repository.get()))


def fetch_stage(identifier: typing.Union[str, UsdAsset], context: Ar.ResolverContext = None, load=Usd.Stage.LoadAll) -> Usd.Stage:
def fetch_stage(identifier: typing.Union[str, UsdAsset], context: Ar.ResolverContext = None, load: Usd.Stage.InitialLoadSet = Usd.Stage.LoadAll) -> Usd.Stage:
"""Retrieve the `stage <https://graphics.pixar.com/usd/docs/api/class_usd_stage.html>`_ whose root `layer <https://graphics.pixar.com/usd/docs/api/class_sdf_layer.html>`_ matches the given ``identifier``.
If the `layer <https://graphics.pixar.com/usd/docs/api/class_sdf_layer.html>`_ does not exist, it is created in the repository.
Expand Down Expand Up @@ -169,7 +170,7 @@ def define_taxon(stage: Usd.Stage, name: str, *, references: tuple[Usd.Prim] = t
return prim


def itaxa(stage: Usd.Stage) -> typing.Generator[Usd.Prim]:
def itaxa(stage: Usd.Stage) -> collections.abc.Iterator[Usd.Prim]:
"""For the given stage, iterate existing taxa under the taxonomy hierarchy."""
return filter(
lambda prim: prim.GetAssetInfoByKey(_ASSETINFO_TAXA_KEY),
Expand All @@ -191,7 +192,7 @@ def _broadcast_root_path(taxon, broadcast_method, scope_path=None):
return scope_path.ReplacePrefix(_CATALOGUE_ROOT_PATH, _BROADCAST_METHOD_RELPATHS[broadcast_method])


def create_many(taxon, names, labels=tuple()) -> typing.List[Usd.Prim]:
def create_many(taxon: Usd.Prim, names: collections.abc.Iterable[str], labels: collections.abc.Iterable[str] = tuple()) -> list[Usd.Prim]:
"""Create a new taxon member for each of the provided names.
When creating hundreds or thousands of members, this provides a considerable performance improvement over :func:`create_unit`.
Expand Down Expand Up @@ -351,7 +352,7 @@ def unit_asset(prim: Usd.Prim) -> Sdf.Layer:
return _find_layer_matching(fields, (i.layer for i in prim.GetPrimStack()))


def spawn_unit(parent, child, path=Sdf.Path.emptyPath, label=""):
def spawn_unit(parent: Usd.Prim, child: Usd.Prim, path: Sdf.Path = Sdf.Path.emptyPath, label: str = "") -> Usd.Prim:
"""Spawn a unit prim as a descendant of another.
* Both parent and child must be existing units in the catalogue.
Expand All @@ -367,7 +368,7 @@ def spawn_unit(parent, child, path=Sdf.Path.emptyPath, label=""):
return spawn_many(parent, child, [path or child.GetName()], [label])[0]


def spawn_many(parent: Usd.Prim, child: Usd.Prim, paths: list[Sdf.Path], labels: list[str] = ()):
def spawn_many(parent: Usd.Prim, child: Usd.Prim, paths: list[Sdf.Path], labels: list[str] = ()) -> list[Usd.Prim]:
"""Spawn many instances of a prim unit as descendants of another.
* Both parent and child must be existing units in the catalogue.
Expand Down Expand Up @@ -407,7 +408,10 @@ def spawn_many(parent: Usd.Prim, child: Usd.Prim, paths: list[Sdf.Path], labels:
with Sdf.ChangeBlock():
# Action of bringing a unit from our catalogue turns parent into an assembly only if child is a model.
if child_is_model and not (parent_model := Usd.ModelAPI(parent)).IsKind(Kind.Tokens.assembly):
parent_model.SetKind(Kind.Tokens.assembly)
try:
parent_model.SetKind(Kind.Tokens.assembly)
except Exception as exc:
raise RuntimeError(f'Could not set kind to "{Kind.Tokens.assembly}" on parent model {parent_model} with current kind: "{parent_model.GetKind()}", when spawning {child} of kind "{Usd.ModelAPI(child).GetKind()}"') from exc

Check warning on line 414 in grill/cook/__init__.py

View check run for this annotation

Codecov / codecov/patch

grill/cook/__init__.py#L413-L414

Added lines #L413 - L414 were not covered by tests
for spawned_unit, label in zip(spawned, labels):
# Use reference for the asset to:
# 1. Make use of instancing as much as possible with fewer prototypes.
Expand Down Expand Up @@ -456,7 +460,7 @@ def _get_id_fields(prim):
return fields


def _find_layer_matching(tokens: typing.Mapping, layers: typing.Iterable[Sdf.Layer]) -> Sdf.Layer:
def _find_layer_matching(tokens: typing.Mapping, layers: collections.abc.Iterable[Sdf.Layer]) -> Sdf.Layer:
"""Find the first layer matching the given identifier tokens.
:raises ValueError: If none of the given layers match the provided tokens.
Expand Down Expand Up @@ -511,7 +515,7 @@ def _inherit_or_specialize_unit(method, context_unit):


@functools.singledispatch
def taxonomy_graph(prims: Usd.Prim, url_id_prefix) -> nx.DiGraph:
def taxonomy_graph(prims: Usd.Prim, url_id_prefix: str) -> nx.DiGraph:
"""Get the hierarchical taxonomy representation of existing prims."""
graph = nx.DiGraph(tooltip="Taxonomy Graph")
graph.graph.update(
Expand All @@ -535,6 +539,6 @@ def taxonomy_graph(prims: Usd.Prim, url_id_prefix) -> nx.DiGraph:


@taxonomy_graph.register(Usd.Stage)
def _(stage: Usd.Stage, url_id_prefix) -> nx.DiGraph:
def _(stage: Usd.Stage, url_id_prefix: str) -> nx.DiGraph:
# Convenience for the stage
return taxonomy_graph(itaxa(stage), url_id_prefix)
29 changes: 23 additions & 6 deletions grill/usd/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def _pruned_prims(prim_range: Usd.PrimRange, predicate):
yield prim


def common_paths(paths: typing.Iterable[Sdf.Path]) -> typing.List[Sdf.Path]:
def common_paths(paths: abc.Iterable[Sdf.Path]) -> list[Sdf.Path]:
"""For the given paths, get those which are the common parents."""
unique = list()
for path in sorted(filter(lambda p: p and not p.IsAbsoluteRootPath(), paths)):
Expand All @@ -56,12 +56,20 @@ def common_paths(paths: typing.Iterable[Sdf.Path]) -> typing.List[Sdf.Path]:
return unique


def iprims(stage: Usd.Stage, root_paths: typing.Iterable[Sdf.Path] = tuple(), prune_predicate: typing.Callable = None, traverse_predicate=Usd.PrimDefaultPredicate) -> typing.Iterator[Usd.Prim]:
"""Convenience function that creates a generator useful for common prim traversals.
def iprims(stage: Usd.Stage, root_paths: abc.Iterable[Sdf.Path] = tuple(), prune_predicate: abc.Callable[[Usd.Prim], bool] = None, traverse_predicate: typing.Union[Usd._Term, Usd._PrimFlagsConjunction] = None) -> abc.Iterator[Usd.Prim]:
"""Convenience function that creates an iterator useful for common :ref:`glossary:stage traversal`.
Refer to the :ref:`glossary:specifier` ins the documentation.
Refer to the :ref:`Specifier <glossary:specifier>` in the documentation.
Without keyword arguments, this is the same as calling `Usd.Stage.Traverse(...)`, so
use that instead when no `root_paths` or `prune_predicates` are needed.
The remaining methods
(e.g. :code:`GetChildren()`) all use a predefined :usdcpp:`Default Predicate <UsdPrimDefaultPredicate>`
"""
if traverse_predicate is None:
traverse_predicate = Usd.PrimDefaultPredicate
if root_paths: # Traverse only specific parts of the stage.
root_paths = common_paths(root_paths)
# Usd.PrimRange already discards invalid prims, so no need to check.
Expand All @@ -76,11 +84,17 @@ def iprims(stage: Usd.Stage, root_paths: typing.Iterable[Sdf.Path] = tuple(), pr


@functools.singledispatch
def edit_context(prim: Usd.Prim, /, query_filter: Usd.PrimCompositionQuery.Filter, arc_predicate: typing.Callable[[Usd.CompositionArc], bool]) -> Usd.EditContext:
def edit_context(prim: Usd.Prim, /, query_filter: Usd.PrimCompositionQuery.Filter, arc_predicate: abc.Callable[[Usd.CompositionArc], bool]) -> Usd.EditContext:
"""Composition arcs target layer stacks. These functions help create EditTargets for the first matching node's root layer stack from prim's composition arcs.
This allows for "chained" context switching while preserving the same stage objects.
Operate on a :usdclass:`prim` and return an :usdclass:`EditContext <edit_context>`.
Refer to the :ref:`glossary:specifier` ins the documentation.
Refer to the :ref:`Specifier <glossary:specifier>` in the documentation.
Refer to the :ref:`Stage <usdglossary-stage>` in the documentation.
.. tip::
You can try the below code snippet on ``USDView`` (or any other USD DCC application)
Expand Down Expand Up @@ -201,6 +215,9 @@ def Sphere "child" (
}
}
Returns:
:usdclass:`EditContext <edit_context>`
"""
# https://blogs.mathworks.com/developer/2015/03/31/dont-get-in-too-deep/
# with write.context(prim, dict(kingdom="assets")):
Expand Down Expand Up @@ -243,8 +260,8 @@ def _(arc, /, path: Sdf.Path, layer: Sdf.Layer) -> Usd.EditContext:
return _edit_context_by_arc(arc.GetPrim(), type(arc), path, layer)


@edit_context.register
def _(variant_set: Usd.VariantSet, /, layer) -> Usd.EditContext:
@edit_context.register(Usd.VariantSet)
def _(variant_set, /, layer: Sdf.Layer) -> Usd.EditContext:
with contextlib.suppress(Tf.ErrorException):
return variant_set.GetVariantEditContext()
# ----- From Pixar -----
Expand Down
4 changes: 2 additions & 2 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,11 @@ include = grill.*
# conda install conda-forge::graphviz
# python -m pip install grill-names>=2.6.0 networkx>=3.4 pydot>=3.0.1 numpy printree PyOpenGL pyside6
# docs dependencies:
# python -m pip install sphinx myst-parser sphinx-toggleprompt sphinx-copybutton sphinx-togglebutton sphinx-hoverxref>=1.4.1 sphinx_autodoc_typehints sphinx-inline-tabs shibuya
# python -m pip install sphinx myst-parser sphinx-toggleprompt sphinx-copybutton sphinx-togglebutton sphinx-hoverxref>=1.4.1 sphinx_autodoc_typehints sphinx-inline-tabs shibuya sphinxcontrib-doxylink
# For EDGEDB (coming up)
# python -m pip install edgedb
# To install packages in editable mode, cd to desired package repo, then:
# python -m pip install -e .

docs = sphinx; myst-parser; sphinx-toggleprompt; sphinx-copybutton; sphinx-togglebutton; sphinx-hoverxref>=1.4.1; sphinx_autodoc_typehints; sphinx-inline-tabs; shibuya; usd-core
docs = sphinx; myst-parser; sphinx-toggleprompt; sphinx-copybutton; sphinx-togglebutton; sphinx-hoverxref>=1.4.1; sphinx_autodoc_typehints; sphinx-inline-tabs; shibuya; usd-core; sphinxcontrib-doxylink
full = PySide6; usd-core; PyOpenGL

0 comments on commit 69830da

Please sign in to comment.