Unverified Commit aa220dbc authored by Mayank Mittal's avatar Mayank Mittal Committed by GitHub

Adds preview features for black formatter (#192)

# Description

Adds `--preview` feature to the black formatter which also takes care of
the string representations in the code. This feature will become the
default setting in the next release of black
(https://github.com/psf/black/issues/1802).

## Type of change

- New feature (non-breaking change which adds functionality)
- This change requires a documentation update

## Checklist

- [x] I have run the [`pre-commit` checks](https://pre-commit.com/) with
`./orbit.sh --format`
- [ ] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [ ] I have updated the changelog and the corresponding version in the
extension's `config/extension.toml` file
parent 36471c3a
......@@ -3,7 +3,7 @@ repos:
rev: 23.3.0
hooks:
- id: black
args: ["--line-length", "120"]
args: ["--line-length", "120", "--preview"]
- repo: https://github.com/pycqa/flake8
rev: 6.0.0
hooks:
......
......@@ -177,7 +177,10 @@ html_theme_options = {
"use_edit_page_button": True,
"show_toc_level": 2,
"use_sidenotes": True,
"announcement": "⚠️This is a pre-release version of Orbit. Please report any issues on <a href='https://github.com/NVIDIA-Omniverse/orbit/issues'>GitHub</a>.",
"announcement": (
"⚠️This is a pre-release version of Orbit. Please report any issues on <a"
" href='https://github.com/NVIDIA-Omniverse/orbit/issues'>GitHub</a>."
),
}
html_show_copyright = True
......
......@@ -216,9 +216,9 @@ class AppLauncher:
if not set(kwargs.keys()).isdisjoint(launcher_args.keys()):
overlapping_args = set(kwargs.keys()).intersection(launcher_args.keys())
raise ValueError(
f"Input `launcher_args` and `kwargs` both provided common attributes: {overlapping_args}. "
"Please ensure that each argument is supplied to only one of them, as the AppLauncher cannot "
"discern priority between them."
f"Input `launcher_args` and `kwargs` both provided common attributes: {overlapping_args}."
" Please ensure that each argument is supplied to only one of them, as the AppLauncher cannot"
" discern priority between them."
)
launcher_args.update(kwargs)
......@@ -300,10 +300,10 @@ class AppLauncher:
config = vars(known)
if len(config) == 0:
print(
"[Warn][AppLauncher]: There are no arguments attached to the ArgumentParser object. "
"If you have your own arguments, please load your own arguments before calling the "
"`AppLauncher.add_app_launcher_args` method. This allows the method to check the validity "
"of the arguments and perform checks for argument names."
"[Warn][AppLauncher]: There are no arguments attached to the ArgumentParser object."
" If you have your own arguments, please load your own arguments before calling the"
" `AppLauncher.add_app_launcher_args` method. This allows the method to check the validity"
" of the arguments and perform checks for argument names."
)
else:
AppLauncher._check_argparser_config_params(config)
......@@ -406,9 +406,9 @@ class AppLauncher:
for key, value in config.items():
if key in applauncher_keys:
raise ValueError(
f"The passed ArgParser object already has the field '{key}'. This field will be added by "
"AppLauncher.add_app_launcher_args(), and should not be added directly. Please remove the "
"argument or rename it to a non-conflicting name."
f"The passed ArgParser object already has the field '{key}'. This field will be added by"
" `AppLauncher.add_app_launcher_args()`, and should not be added directly. Please remove the"
" argument or rename it to a non-conflicting name."
)
# check that type of the passed keys are valid
simulationapp_keys = set(AppLauncher._SIMULATIONAPP_CONFIG_TYPES.keys())
......@@ -418,9 +418,9 @@ class AppLauncher:
expected_types = AppLauncher._SIMULATIONAPP_CONFIG_TYPES[key]
if type(value) not in set(expected_types):
raise ValueError(
f"Invalid value type for the argument '{key}': {given_type}. Expected one of {expected_types}, "
f"if intended to be ingested by the SimulationApp object. Please change the type if this "
"intended for the SimulationApp or change the name of the argument to avoid name conflicts."
f"Invalid value type for the argument '{key}': {given_type}. Expected one of {expected_types},"
" if intended to be ingested by the SimulationApp object. Please change the type if this"
" intended for the SimulationApp or change the name of the argument to avoid name conflicts."
)
# Print out values which will be used
print(f"[INFO][AppLauncher]: The argument '{key}' will be used to configure the SimulationApp.")
......@@ -441,8 +441,8 @@ class AppLauncher:
# Value checking on LIVESTREAM
if livestream_env not in livestream_valid_vals:
raise ValueError(
f"Invalid value for environment variable `LIVESTREAM`: {livestream_env} . "
f"Expected: {livestream_valid_vals}."
f"Invalid value for environment variable `LIVESTREAM`: {livestream_env} ."
f" Expected: {livestream_valid_vals}."
)
# We allow livestream kwarg to supersede LIVESTREAM envvar
if livestream_arg >= 0:
......@@ -450,13 +450,13 @@ class AppLauncher:
self._livestream = livestream_arg
# print info that we overrode the env-var
print(
f"[INFO][AppLauncher]: Input keyword argument `livestream={livestream_arg}` has overridden "
f"the environment variable `LIVESTREAM={livestream_env}`."
f"[INFO][AppLauncher]: Input keyword argument `livestream={livestream_arg}` has overridden"
f" the environment variable `LIVESTREAM={livestream_env}`."
)
else:
raise ValueError(
f"Invalid value for input keyword argument `livestream`: {livestream_arg} . "
f"Expected: {livestream_valid_vals}."
f"Invalid value for input keyword argument `livestream`: {livestream_arg} ."
f" Expected: {livestream_valid_vals}."
)
else:
self._livestream = livestream_env
......@@ -472,8 +472,7 @@ class AppLauncher:
# Value checking on HEADLESS
if headless_env not in headless_valid_vals:
raise ValueError(
f"Invalid value for environment variable `HEADLESS`: {headless_env} . "
f"Expected: {headless_valid_vals}."
f"Invalid value for environment variable `HEADLESS`: {headless_env} . Expected: {headless_valid_vals}."
)
# We allow headless kwarg to supersede HEADLESS envvar if headless_arg does not have the default value
# Note: Headless is always true when livestreaming
......@@ -485,13 +484,13 @@ class AppLauncher:
# inform who has toggled the headless flag
if self._livestream == livestream_arg:
print(
f"[INFO][AppLauncher]: Input keyword argument `livestream={self._livestream}` has implicitly "
f"overridden the environment variable `HEADLESS={headless_env}` to True."
f"[INFO][AppLauncher]: Input keyword argument `livestream={self._livestream}` has implicitly"
f" overridden the environment variable `HEADLESS={headless_env}` to True."
)
elif self._livestream == livestream_env:
print(
f"[INFO][AppLauncher]: Environment variable `LIVESTREAM={self._livestream}` has implicitly "
f"overridden the environment variable `HEADLESS={headless_env}` to True."
f"[INFO][AppLauncher]: Environment variable `LIVESTREAM={self._livestream}` has implicitly"
f" overridden the environment variable `HEADLESS={headless_env}` to True."
)
else:
# Headless needs to be a bool to be ingested by SimulationApp
......@@ -515,8 +514,8 @@ class AppLauncher:
self._ros = ros_arg
# print info that we overrode the env-var
print(
f"[INFO][AppLauncher]: Input keyword argument `ros={ros_arg}` has overridden "
f"the environment variable `ROS_ENABLED={ros_env}`."
f"[INFO][AppLauncher]: Input keyword argument `ros={ros_arg}` has overridden"
f" the environment variable `ROS_ENABLED={ros_env}`."
)
else:
raise ValueError(
......
......@@ -508,8 +508,8 @@ class Articulation(RigidObject):
# check if any joints are found
if len(joint_names) == 0:
raise ValueError(
f"No joints found for actuator group: {actuator_name} with joint name expression: "
f"{actuator_cfg.joint_names_expr}."
f"No joints found for actuator group: {actuator_name} with joint name expression:"
f" {actuator_cfg.joint_names_expr}."
)
# for efficiency avoid indexing when over all indices
if len(joint_names) == self.num_joints:
......@@ -524,8 +524,8 @@ class Articulation(RigidObject):
)
# log information on actuator groups
carb.log_info(
f"Actuator collection: {actuator_name} with model '{actuator_cfg.class_type.__name__}' and "
f"joint names: {joint_names} [{joint_ids}]."
f"Actuator collection: {actuator_name} with model '{actuator_cfg.class_type.__name__}' and"
f" joint names: {joint_names} [{joint_ids}]."
)
# store actuator group
self.actuators[actuator_name] = actuator
......
......@@ -448,7 +448,8 @@ def apply_physics_material(prim_path: str, material_path: str, weaker_than_desce
has_particle_system = prim.IsA(PhysxSchema.PhysxParticleSystem)
if not (has_collider or has_deformable_body or has_particle_system):
raise ValueError(
f"Cannot apply physics material on prim '{prim_path}'. It is neither a collider, nor a deformable body, nor a particle system."
f"Cannot apply physics material on prim '{prim_path}'. It is neither a collider,"
" nor a deformable body, nor a particle system."
)
# obtain material binding API
if prim.HasAPI(UsdShade.MaterialBindingAPI):
......
......@@ -376,7 +376,10 @@ class ObservationManager:
# Think: Check for cases when kwargs are set inside the function?
if len(args) > 2:
if set(args[2:]) != set(term_params):
msg = f"Observation term '{term_name}' expects parameters: {args[2:]}, but {term_params} provided."
msg = (
f"Observation term '{term_name}' expects parameters: {args[2:]}, but"
f" {term_params} provided."
)
raise ValueError(msg)
# add function to list
self._group_obs_term_names[group_name].append(term_name)
......
......@@ -52,7 +52,8 @@ class BinaryJointAction(ActionTerm):
self._num_joints = len(self._joint_ids)
# log the resolved joint names for debugging
carb.log_info(
f"Resolved joint names for the action term {self.__class__.__name__}: {self._joint_names} [{self._joint_ids}]"
f"Resolved joint names for the action term {self.__class__.__name__}:"
f" {self._joint_names} [{self._joint_ids}]"
)
# create tensors for raw and processed actions
......
......@@ -59,7 +59,8 @@ class JointAction(ActionTerm):
self._num_joints = len(self._joint_ids)
# log the resolved joint names for debugging
carb.log_info(
f"Resolved joint names for the action term {self.__class__.__name__}: {self._joint_names} [{self._joint_ids}]"
f"Resolved joint names for the action term {self.__class__.__name__}:"
f" {self._joint_names} [{self._joint_ids}]"
)
# Avoid indexing across all joints for efficiency
......
......@@ -88,7 +88,8 @@ class NonHolonomicAction(ActionTerm):
self._joint_names = [x_joint_name[0], y_joint_name[0], yaw_joint_name[0]]
# log info for debugging
carb.log_info(
f"Resolved joint names for the action term {self.__class__.__name__}: {self._joint_names} [{self._joint_ids}]"
f"Resolved joint names for the action term {self.__class__.__name__}:"
f" {self._joint_names} [{self._joint_ids}]"
)
carb.log_info(
f"Resolved body name for the action term {self.__class__.__name__}: {self._body_name} [{self._body_idx}]"
......
......@@ -68,7 +68,8 @@ class DifferentialInverseKinematicsAction(ActionTerm):
# log info for debugging
carb.log_info(
f"Resolved joint names for the action term {self.__class__.__name__}: {self._joint_names} [{self._joint_ids}]"
f"Resolved joint names for the action term {self.__class__.__name__}:"
f" {self._joint_names} [{self._joint_ids}]"
)
carb.log_info(
f"Resolved body name for the action term {self.__class__.__name__}: {self._body_name} [{self._body_idx}]"
......
......@@ -254,7 +254,8 @@ class ActionManager(ManagerBase):
# check valid type
if not isinstance(term_cfg, ActionTermCfg):
raise TypeError(
f"Configuration for the term '{term_name}' is not of type ActionTermCfg. Received '{type(term_cfg)}'."
f"Configuration for the term '{term_name}' is not of type ActionTermCfg."
f" Received: '{type(term_cfg)}'."
)
# create the action term
term = term_cfg.class_type(term_cfg, self._env)
......
......@@ -148,7 +148,8 @@ class CurriculumManager(ManagerBase):
# check if the term is a valid term config
if not isinstance(term_cfg, CurriculumTermCfg):
raise TypeError(
f"Configuration for the term '{term_name}' is not of type CurriculumTermCfg. Received '{type(term_cfg)}'."
f"Configuration for the term '{term_name}' is not of type CurriculumTermCfg."
f" Received: '{type(term_cfg)}'."
)
# resolve common parameters
self._resolve_common_term_cfg(term_name, term_cfg, min_argc=2)
......
......@@ -112,7 +112,8 @@ class ManagerBase(ABC):
# check if the term is a valid term config
if not isinstance(term_cfg, ManagerBaseTermCfg):
raise TypeError(
f"Configuration for the term '{term_name}' is not of type ManagerBaseTermCfg. Received '{type(term_cfg)}'."
f"Configuration for the term '{term_name}' is not of type ManagerBaseTermCfg."
f" Received: '{type(term_cfg)}'."
)
# iterate over all the entities and parse the joint and body names
for key, value in term_cfg.params.items():
......
......@@ -170,7 +170,8 @@ class ObservationManager(ManagerBase):
# check if the term is a curriculum term
if not isinstance(group_cfg, ObservationGroupCfg):
raise TypeError(
f"Observation group '{group_name}' is not of type 'ObservationGroupCfg'. Received '{type(group_cfg)}'."
f"Observation group '{group_name}' is not of type 'ObservationGroupCfg'."
f" Received: '{type(group_cfg)}'."
)
# initialize list for the group settings
self._group_obs_term_names[group_name] = list()
......@@ -194,7 +195,8 @@ class ObservationManager(ManagerBase):
continue
if not isinstance(term_cfg, ObservationTermCfg):
raise TypeError(
f"Configuration for the term '{term_name}' is not of type ObservationTermCfg. Received '{type(term_cfg)}'."
f"Configuration for the term '{term_name}' is not of type ObservationTermCfg."
f" Received: '{type(term_cfg)}'."
)
# resolve common terms in the config
self._resolve_common_term_cfg(f"{group_name}/{term_name}", term_cfg, min_argc=1)
......
......@@ -128,7 +128,8 @@ class RandomizationManager(ManagerBase):
if mode == "interval":
if dt is None:
raise ValueError(
f"Randomization mode '{mode}' requires the time step of the environment to be passed to the randomization manager."
f"Randomization mode '{mode}' requires the time step of the environment"
" to be passed to the randomization manager."
)
# extract time left for this term
time_left = self._interval_mode_time_left[index]
......@@ -167,7 +168,8 @@ class RandomizationManager(ManagerBase):
# check for valid config type
if not isinstance(term_cfg, RandomizationTermCfg):
raise TypeError(
f"Configuration for the term '{term_name}' is not of type RandomizationTermCfg. Received '{type(term_cfg)}'."
f"Configuration for the term '{term_name}' is not of type RandomizationTermCfg."
f" Received: '{type(term_cfg)}'."
)
# resolve common parameters
self._resolve_common_term_cfg(term_name, term_cfg, min_argc=2)
......
......@@ -159,12 +159,14 @@ class RewardManager(ManagerBase):
# check for valid config type
if not isinstance(term_cfg, RewardTermCfg):
raise TypeError(
f"Configuration for the term '{term_name}' is not of type RewardTermCfg. Received '{type(term_cfg)}'."
f"Configuration for the term '{term_name}' is not of type RewardTermCfg."
f" Received: '{type(term_cfg)}'."
)
# check for valid weight type
if not isinstance(term_cfg.weight, (float, int)):
raise TypeError(
f"Weight for the term '{term_name}' is not of type float or int. Received '{type(term_cfg.weight)}'."
f"Weight for the term '{term_name}' is not of type float or int."
f" Received: '{type(term_cfg.weight)}'."
)
# resolve common parameters
self._resolve_common_term_cfg(term_name, term_cfg, min_argc=1)
......
......@@ -161,7 +161,8 @@ class TerminationManager(ManagerBase):
# check for valid config type
if not isinstance(term_cfg, TerminationTermCfg):
raise TypeError(
f"Configuration for the term '{term_name}' is not of type TerminationTermCfg. Received '{type(term_cfg)}'."
f"Configuration for the term '{term_name}' is not of type TerminationTermCfg."
f" Received: '{type(term_cfg)}'."
)
# resolve common parameters
self._resolve_common_term_cfg(term_name, term_cfg, min_argc=1)
......
......@@ -397,8 +397,8 @@ class Camera(SensorBase):
# Check that sizes are correct
if self._view.count != self._num_envs:
raise RuntimeError(
f"Number of camera prims in the view ({self._view.count}) does not match the number of environments "
f"({self._num_envs})."
f"Number of camera prims in the view ({self._view.count}) does not match"
f" the number of environments ({self._num_envs})."
)
# Create all indices buffer
......
......@@ -197,7 +197,7 @@ class ContactSensor(SensorBase):
# check that contact reporter succeeded
if self._num_bodies != len(body_names):
raise RuntimeError(
f"Failed to initialize contact reporter for specified bodies."
"Failed to initialize contact reporter for specified bodies."
f"\n\tInput prim path : {self.cfg.prim_path}"
f"\n\tResolved prim paths: {body_names_regex}"
)
......
......@@ -428,8 +428,8 @@ def activate_contact_sensors(prim_path: str, threshold: float = 0.0, stage: Usd.
# check if no contact sensors were found
if num_contact_sensors == 0:
raise ValueError(
f"No contact sensors added to the prim: '{prim_path}'. This means that no rigid bodies "
"are present under this prim. Please check the prim path."
f"No contact sensors added to the prim: '{prim_path}'. This means that no rigid bodies"
" are present under this prim. Please check the prim path."
)
# success
return True
......@@ -47,7 +47,8 @@ def random_uniform_terrain(difficulty: float, cfg: hf_terrains_cfg.HfRandomUnifo
cfg.downsampled_scale = cfg.horizontal_scale
elif cfg.downsampled_scale < cfg.horizontal_scale:
raise ValueError(
f"Downsampled scale must be larger than or equal to the horizontal scale: {cfg.downsampled_scale} < {cfg.horizontal_scale}."
"Downsampled scale must be larger than or equal to the horizontal scale:"
f" {cfg.downsampled_scale} < {cfg.horizontal_scale}."
)
# switch parameters to discrete units
......
......@@ -36,8 +36,8 @@ def height_field_to_mesh(func: Callable) -> Callable:
# check valid border width
if cfg.border_width > 0 and cfg.border_width < cfg.horizontal_scale:
raise ValueError(
f"The border width ({cfg.border_width}) must be greater than or equal to the "
f"horizontal scale ({cfg.horizontal_scale})."
f"The border width ({cfg.border_width}) must be greater than or equal to the"
f" horizontal scale ({cfg.horizontal_scale})."
)
# allocate buffer for height field (with border)
width_pixels = int(cfg.size[0] / cfg.horizontal_scale) + 1
......
......@@ -286,9 +286,9 @@ def _process_mutable_types(cls):
# note: mainly for debugging purposes
if len(class_members) != len(ann):
raise ValueError(
f"In class '{cls.__name__}', number of annotations ({len(ann)}) does not match number of class "
f"members ({len(class_members)}). Please check that all class members have type annotations and/or "
"a default value. If you don't want to specify a default value, please use the literal `dataclasses.MISSING`."
f"In class '{cls.__name__}', number of annotations ({len(ann)}) does not match number of class members"
f" ({len(class_members)}). Please check that all class members have type annotations and/or a default"
" value. If you don't want to specify a default value, please use the literal `dataclasses.MISSING`."
)
# iterate over annotations and add field factory for mutable types
for key in ann:
......
......@@ -105,7 +105,8 @@ def update_class_from_dict(obj, data: dict[str, Any], _ns: str = "") -> None:
# check length of value to be safe
if len(obj_mem) != len(value) and obj_mem is not None:
raise ValueError(
f"[Config]: Incorrect length under namespace: {key_ns}. Expected: {len(obj_mem)}, Received: {len(value)}."
f"[Config]: Incorrect length under namespace: {key_ns}."
f" Expected: {len(obj_mem)}, Received: {len(value)}."
)
# set value
setattr(obj, key, value)
......@@ -118,7 +119,8 @@ def update_class_from_dict(obj, data: dict[str, Any], _ns: str = "") -> None:
setattr(obj, key, value)
else:
raise ValueError(
f"[Config]: Incorrect type under namespace: {key_ns}. Expected: {type(obj_mem)}, Received: {type(value)}."
f"[Config]: Incorrect type under namespace: {key_ns}."
f" Expected: {type(obj_mem)}, Received: {type(value)}."
)
else:
raise KeyError(f"[Config]: Key not found under namespace: {key_ns}.")
......
......@@ -200,7 +200,8 @@ def resolve_matching_names(keys: str | Sequence[str], list_of_strings: Sequence[
# check if match already found
if target_strings_match_found[target_index]:
raise ValueError(
f"Multiple matches for '{potential_match_string}': '{target_strings_match_found[target_index]}' and '{re_key}'!"
f"Multiple matches for '{potential_match_string}':"
f" '{target_strings_match_found[target_index]}' and '{re_key}'!"
)
# add to list
target_strings_match_found[target_index] = re_key
......@@ -265,7 +266,8 @@ def resolve_matching_names_values(
# check if match already found
if target_strings_match_found[target_index]:
raise ValueError(
f"Multiple matches for '{potential_match_string}': '{target_strings_match_found[target_index]}' and '{re_key}'!"
f"Multiple matches for '{potential_match_string}':"
f" '{target_strings_match_found[target_index]}' and '{re_key}'!"
)
# add to list
target_strings_match_found[target_index] = re_key
......
......@@ -86,8 +86,8 @@ class AntEnv(IsaacEnv):
assets_root_path = nucleus_utils.get_assets_root_path()
if assets_root_path is None:
raise RuntimeError(
"Unable to access the Nucleus server from Omniverse. For more information, please check: "
"https://docs.omniverse.nvidia.com/app_isaacsim/app_isaacsim/overview.html#omniverse-nucleus"
"Unable to access the Nucleus server from Omniverse. For more information, please check:"
" https://docs.omniverse.nvidia.com/app_isaacsim/app_isaacsim/overview.html#omniverse-nucleus"
)
# ground plane
kit_utils.create_ground_plane(
......
......@@ -74,8 +74,8 @@ class CartpoleEnv(IsaacEnv):
assets_root_path = nucleus_utils.get_assets_root_path()
if assets_root_path is None:
raise RuntimeError(
"Unable to access the Nucleus server from Omniverse. For more information, please check: "
"https://docs.omniverse.nvidia.com/app_isaacsim/app_isaacsim/overview.html#omniverse-nucleus"
"Unable to access the Nucleus server from Omniverse. For more information, please check:"
" https://docs.omniverse.nvidia.com/app_isaacsim/app_isaacsim/overview.html#omniverse-nucleus"
)
# ground plane
kit_utils.create_ground_plane("/World/defaultGroundPlane")
......
......@@ -88,8 +88,8 @@ class HumanoidEnv(IsaacEnv):
assets_root_path = nucleus_utils.get_assets_root_path()
if assets_root_path is None:
raise RuntimeError(
"Unable to access the Nucleus server from Omniverse. For more information, please check: "
"https://docs.omniverse.nvidia.com/app_isaacsim/app_isaacsim/overview.html#omniverse-nucleus"
"Unable to access the Nucleus server from Omniverse. For more information, please check:"
" https://docs.omniverse.nvidia.com/app_isaacsim/app_isaacsim/overview.html#omniverse-nucleus"
)
# ground plane
kit_utils.create_ground_plane(
......
......@@ -154,7 +154,8 @@ def main():
collector_interface.add("success", info["is_success"])
except KeyError:
raise RuntimeError(
f"Only goal-conditioned environment supported. No attribute named 'is_success' found in {list(info.keys())}."
"Only goal-conditioned environment supported. No attribute named"
f" 'is_success' found in {list(info.keys())}."
)
# flush data from collector for successful environments
reset_env_ids = dones.nonzero(as_tuple=False).squeeze(-1)
......
......@@ -110,9 +110,11 @@ if __name__ == "__main__":
"--filter_key",
type=str,
default=None,
help="if provided, split the subset of trajectories in the file that correspond to\
this filter key into a training and validation set of trajectories, instead of\
splitting the full set of trajectories",
help=(
"If provided, split the subset of trajectories in the file that correspond to this filter key"
" into a training and validation set of trajectories, instead of splitting the full set of"
" trajectories."
),
)
parser.add_argument("--ratio", type=float, default=0.1, help="validation ratio, in (0, 1)")
args = parser.parse_args()
......
......@@ -43,8 +43,7 @@ def parse_cli_args():
# When --help or no args are given, print this help
usage_text = (
"Run blender in background mode with this script:\n"
"\tblender --background --python " + __file__ + " -- [options]"
f"Run blender in background mode with this script:\n\tblender --background --python {__file__} -- [options]"
)
parser = argparse.ArgumentParser(description=usage_text)
# Add arguments
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment