def bake_from_request(request, directory, force=False): """Generate symlinks for a user's given Rez package request. Args: request (list[str]): The Rez packages to make into a Rez resolve. directory (str): The absolute path to a directory on-disk to place the symlinks under. This path can later be fed directly into REZ_PACKAGES_PATH to reproduce the environment. force (bool, optional): If True, delete any symlinks that may already exist. If False and symlinks exist, normal exceptions will be raised. Default is False. Raises: ValueError: If `request` contains 1+ package that does not exist. """ try: context = resolved_context.ResolvedContext(request) except exceptions.PackageFamilyNotFoundError: _LOGGER.exception('Request "%s" was not found.', request) raise ValueError( 'Request "{request}" was not found.'.format(request=request)) paths = pather.get_paths_from_environment(context.get_environ().items()) make_symlinks(paths, directory, force=force)
def test_installed_package_no_variants(self): """Create a Rez package with no variants and get the folders affecting PYTHONPATH.""" dependencies = self._make_test_dependencies() package, root = self._make_fake_rez_install_package( "some_fake_package", "1.0.0", textwrap.dedent("""\ name = "some_fake_package" version = "1.0.0" def commands(): import os env.PYTHONPATH.append(os.path.join("{root}", "python")) """), ) context = resolved_context.ResolvedContext( ["{package.name}==1.0.0".format(package=package)], package_paths=[inspection.get_packages_path_from_package(package)] + dependencies + config.packages_path, # pylint: disable=no-member ) python_files = inspection.get_package_python_paths( package, context.get_environ().get("PYTHONPATH", "").split(os.pathsep)) self.assertEqual( {os.path.join(root, "some_fake_package", "1.0.0", "python")}, python_files)
def python_config_jam(): # Lookup all installed python packages from rez import packages, resolved_context versions = list() info_cmd = ("python %s get-python-info" % sys.argv[0]) for python_pkg in packages.iter_packages("python"): full_ver = str(python_pkg.version) python_ver = ".".join(full_ver.split(".")[:2]) # exclude patch ver versions.append(python_ver) versions.sort() # Only use python-27 for now (for USD) # because cmake files only link to the version which built latest. versions = ["2.7"] for python_ver in versions: python_name = "python-%s" % python_ver rc = resolved_context.ResolvedContext([python_name], caching=False) popen = rc.execute_shell(command=info_cmd) popen.wait() return versions
def install(package, root, build_path, version=''): '''Install a given package into `build_path`. Args: root (str): The absolute path to where all installed packages live. build_path (str): The absolute path to install the package to. version (str, optional): The specific version to get the path of. If no version is given, the latest version is used, instead. Default: "". ''' package = package.split('-')[0] LOGGER.debug('Installing "%s".', package) try: # Request the specific package-version resolved_context.ResolvedContext(['{}-{}'.format(package, version)]) except PACKAGE_EXCEPTIONS: build_package_recursively(root, package, version=version, build_path=build_path)
def build_package_recursively(root, package, version='', build_path=''): '''Build a package by building its required packages recursively. Basically the logic goes like this: - Evaluate if a package has been built. If it has been built, do nothing. - If a package has no requirements, build it. - If a package has requirements and that requirement isn't installed, then evaluate and build it, too. - Repeat until all packages are built. Raises: RuntimeError: If a package was build incorrectly or if an attempt to build a package failed. ''' def build_definition(definition, build_path): try: multipurpose_helper.build( os.path.dirname(definition.__file__), install_path=build_path, ) except Exception: # TODO : Consider deleting the contents of # `os.path.dirname(definition.__file__)` # before erroring out, here # message = 'Definition "%s" failed to build.' LOGGER.exception(message, definition.__file__) raise RuntimeError(message % definition.__file__) LOGGER.debug('Building package "%s".', package) definition = get_package_definition(root, package, version) if not definition: raise RuntimeError('No definition file could be loaded.') pkg = make_package(definition, build_path) requirements = pkg.get_package().requires for requirement in requirements: try: resolved_context.ResolvedContext([requirement]) except PACKAGE_EXCEPTIONS: requirement_package, requirement_version = str(requirement).split( '-') build_package_recursively(root, requirement_package, requirement_version, build_path=build_path) # Now that all of the requirements are installed, install this package build_definition(definition, build_path)
def test_installed_package_with_variants(self): """Create a Rez package with variants and get the folders affecting PYTHONPATH.""" dependencies = self._make_test_dependencies() package = self._make_fake_rez_source_package( "some_fake_package", textwrap.dedent("""\ name = "some_fake_package" version = "1.0.0" variants = [["some_dependency-1", "another_one-2.3"], ["another_one-2.3"]] build_command = "echo 'foo'" def commands(): import os env.PYTHONPATH.append(os.path.join("{root}", "python")) """), ) install_path = tempfile.mkdtemp(suffix="_install_path") self.delete_item_later(install_path) build_package = creator.build( package, install_path, packages_path=dependencies + config.packages_path, # pylint: disable=no-member quiet=True, ) context = resolved_context.ResolvedContext( [ "{build_package.name}==1.0.0".format( build_package=build_package) ], package_paths=[ inspection.get_packages_path_from_package(build_package) ] + dependencies + config.packages_path, # pylint: disable=no-member ) python_files = inspection.get_package_python_paths( build_package, context.get_environ().get("PYTHONPATH", "").split(os.pathsep)) self.assertEqual( { os.path.join( install_path, "some_fake_package", "1.0.0", "another_one-2.3", "python", ) }, python_files, )
def execute(self, app_path, app_args, version, engine_name, **kwargs): """ The execute functon of the hook will be called to start the required application :param app_path: (str) The path of the application executable :param app_args: (str) Any arguments the application may require :param version: (str) version of the application being run if set in the "versions" settings of the Launcher instance, otherwise None :param engine_name (str) The name of the engine associated with the software about to be launched. :returns: (dict) The two valid keys are 'command' (str) and 'return_code' (int). """ if engine_name == "tk-photoshopcc": cmd = "start /B \"App\" \"%s\" %s" % (app_path, app_args) exit_code = os.system(cmd) return {"command": cmd, "return_code": exit_code } app_name = ENGINES[engine_name] context = self.tank.context_from_path(self.tank.project_path) project = context.project sg = self.tank.shotgun system = sys.platform adapter = get_adapter(platform.system()) packages = get_rez_packages(sg,app_name,version,system,project) try: import rez as _ except ImportError: rez_path = adapter.get_rez_module_root() if not rez_path: raise EnvironmentError('rez is not installed and could not be automatically found. Cannot continue.') sys.path.append(rez_path) from rez import resolved_context if not packages: self.logger.debug('No rez packages were found. The default boot, instead.') command = adapter.get_command(app_path, app_args) return_code = os.system(command) return {'command': command, 'return_code': return_code} context = resolved_context.ResolvedContext(packages) return adapter.execute(context, app_args,app_name)
def _get_package_root(package, package_paths): root = os.getenv(inspection.get_root_key(package.name), "") if root: return root context = resolved_context.ResolvedContext( ["{package.name}=={package.version}".format(package=package)], package_paths=package_paths, ) environment = context.get_environ() return environment[inspection.get_root_key(package.name)]
def _make_test_case(self, name, text, extra_paths=None): """Create a Rez package that has some Python files inside of it. This method exists to make unittesting a bit easier to understand. Note: This method creates Python files whether `text` actually appends to PYTHONPATH or not. They just get ignored by unittests, in that case. Args: name (str): The name of the Rez package family to make. text (str): The source code used for the package.py file. extra_paths (list[str], optional): A list of paths used to search for Rez packages during the Rez resolve that this function calls. If no paths are given, Rez will default to :attr:`rez.config.packages_path` instead. Default is None. Returns: tuple[set[str], str]: The found Python files (excluding __init__.py files) and the parent folder where the created package.py goes. """ if not extra_paths: extra_paths = [] package = self._make_fake_rez_source_package(name, text) root = finder.get_package_root(package) python_root = os.path.join(root, "python") os.makedirs(python_root) open(os.path.join(python_root, "thing.py"), "w").close() open(os.path.join(python_root, "another.py"), "w").close() context = resolved_context.ResolvedContext( ["{package.name}==".format(package=package)], package_paths=[inspection.get_packages_path_from_package(package)] + extra_paths + config.packages_path, # pylint: disable=no-member ) paths = inspection.get_package_python_paths( package, context.get_environ().get("PYTHONPATH", "").split(os.pathsep)) return paths, root
def test_one_error_001(self): """Check that calling the tool from commmand-line contains only a single error. Run ``rez-documentation-check`` from command-line (in a subprocess). Raises: RuntimeError: If the called command-line tool fails for any reason. """ paths, importer_package = self._setup_packages() local_request = "{package.name}==" package_paths = list(paths) + config.packages_path # pylint: disable=no-member context = resolved_context.ResolvedContext( [ _THIS_PACKAGE.name, local_request.format(package=importer_package) ], package_paths=package_paths, ) # This section is a bit complex. Basically, if a user was # running rez-documentation-check, normally every dependency of # a package would either already be released or a part of the # user's packages. To mimic this assumption, we need to define # REZ_PACKAGES_PATH to include the importer_package's path + # foo_bar, which is one of its dependenices. # command = _make_check_command(importer_package) base = "REZ_PACKAGES_PATH={packages_path}".format( packages_path=(os.pathsep).join(package_paths)) process = context.execute_shell(command=base + " " + command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = process.communicate() if stderr: raise RuntimeError(stderr) expected = textwrap.dedent("""\ Missing intersphinx links were found. Please add the mapping below to your Sphinx conf.py file. intersphinx_mapping = { 'foo_bar': ('https://some_path.com', None)} """) self.assertTrue(expected in stdout.decode("utf-8"))
def _test_command(command): """Run rez-documentation-check in a fake command-line environment. Args: command (str): The rez-documentation-check command that would run on command-line. Returns: tuple[str, str]: The stdout and stderr that was created by the given `command`. """ context = resolved_context.ResolvedContext( [_THIS_PACKAGE.name, "python-2"]) process = context.execute_shell(command=command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) return process.communicate()
def execute_action(self, name, params, sg_publish_data): """ Execute a given action. The data sent to this be method will represent one of the actions enumerated by the generate_actions method. :param name: Action name string representing one of the items returned by generate_actions. :param params: Params data, as specified by generate_actions. :param sg_publish_data: Shotgun data dictionary with all the standard publish fields. :returns: No return value expected. """ app = self.parent app.log_debug("Execute action called for action %s. " "Parameters: %s. Publish Data: %s" % (name, params, sg_publish_data)) # resolve path - forward slashes on all platforms in Nuke path = self.get_publish_path(sg_publish_data).replace(os.path.sep, "/") if name == "read_channel": self._select_channel_view(path, sg_publish_data) if name == "rv": rez_path = self._get_rez_module() sys.path.append(rez_path) from rez import resolved_context packages = ("rv", ) context = resolved_context.ResolvedContext(packages) command = "rv" command += ' {path}'.format(path=path) context.execute_shell( command=command, stdin=False, block=False, ) else: super(NukeAddActions, self).execute_action(name, params, sg_publish_data)
def _get_context(packages): '''Get a Rez environment that satisfies the given packages, if possible. Args: packages (list[str]): All of the packages that must be contained in the context. It can include the name-version of the package or just the package name. Example: ["nuke-11.2v3"] or ["nuke"]. Returns: `resolved_context.ResolvedContext` or NoneType: The found package countext, assuming `packages` has already been built correctly. ''' from rez import resolved_context from rezzurect import manager try: return resolved_context.ResolvedContext(packages) except manager.PACKAGE_EXCEPTIONS: return
def execute_action(self, name, params, sg_publish_data): """ Execute a given action. The data sent to this be method will represent one of the actions enumerated by the generate_actions method. :param name: Action name string representing one of the items returned by generate_actions. :param params: Params data, as specified by generate_actions. :param sg_publish_data: Shotgun data dictionary with all the standard publish fields. :returns: No return value expected. """ app = self.parent app.log_debug("Execute action called for action %s. " "Parameters: %s. Publish Data: %s" % (name, params, sg_publish_data)) # resolve path path = self.get_publish_path(sg_publish_data) if name == "open_project": self._open_project(path, sg_publish_data) if name == "import_look_file": self._open_project(path, sg_publish_data) if name == "create_node_Alembic_In": self._create_node("ABC", path, sg_publish_data, asset_parameter="abcAsset") if name == "create_node_ImageRead": self._create_node("ImageRead", path, sg_publish_data, asset_parameter="file") if name == "create_pxrusd_in": self._create_node("USD", path, sg_publish_data, asset_parameter="fileName") if name == "create_scenegraphXml": self._create_node("XML", path, sg_publish_data, asset_parameter="asset") if name == "viewer": rez_path = self._get_rez_module() sys.path.append(rez_path) from rez import resolved_context packages = ("usd", "PySide") context = resolved_context.ResolvedContext(packages) command = "usdview" command += ' {path}'.format(path=path) context.execute_shell( command=command, stdin=False, block=False, ) if name == "copy_path": rez_path = self._get_rez_module() sys.path.append(rez_path) from rez import resolved_context packages = ("pyperclip", ) context = resolved_context.ResolvedContext(packages) sys.path.append(context.get_environ()['PYTHONPATH']) import pyperclip pyperclip.copy(path)
def has_python_package( # pylint: disable=too-many-branches,too-many-locals package, paths=None, allow_build=True, allow_current_context=False): """Check if the given Rez package has at least one Python package inside of it. Note: This function assumes that `package` is a built package. Otherwise, it may return incorrect results. Important: This function assumes that all Python packages set / append to the PYTHONPATH environment variable. It uses that environment variable to find Python packages. Args: package (:class:`rez.packages_.Package`): The Rez package to check for Python packages. paths (list[str], optional): The locations on-disk that will be used to any Rez-environment-related work. Some plugins need these paths for resolving a context, for example. If no paths are given, :attr:`rez.config.config.packages_path` is used instead. Default is None. allow_build (bool, optional): If `package` is a source Rez package and no importable Python modules could be found and this parameter is True, build the package to see if its generated content includes a Python module. If False though, this function will simply return False without building the Rez source package. Default is True. Raises: ValueError: If `package` is not a Rez package. Returns: bool: If a Python package is detected. """ from . import creator # Avoiding a cyclic import if not hasattr(package, "name") or not hasattr(package, "version"): raise ValueError( 'Object "{package}" is not a valid Rez package.'.format( package=package)) if not paths: paths = config.packages_path # pylint: disable=no-member version = "" is_built = is_built_package(package) if is_built: version = package.version if allow_current_context and in_valid_context(package): environment = os.environ.get("PYTHONPATH", "").split(os.pathsep) else: context = resolved_context.ResolvedContext( [ "{package.name}=={version}".format(package=package, version=version) ], package_paths=[get_packages_path_from_package(package)] + paths, ) environment = context.get_environ().get("PYTHONPATH", "").split(os.pathsep) paths = get_package_python_paths(package, environment) # All zipped .egg files as valid Python "packages" for path in paths: if path.endswith(".egg") and os.path.isfile(path): return True for root_path in paths: for _, _, files in os.walk(root_path): for file_path in files: if file_path == "__init__.py": continue if file_path.endswith(".py"): return True if is_built or not allow_build: return False # If the package is a source package and PYTHONPATH is defined but # no path was found, it may actually be that the Python files are # generated on-build (such as C++ files with Python bindings). To # find out, we need to run this function again, but with the built # package. # build_directory = tempfile.mkdtemp( suffix="_some_temporary_rez_build_package") build_package = creator.build(package, build_directory, quiet=True) # Reference: https://stackoverflow.com/questions/3850261/doing-something-before-program-exit atexit.register(functools.partial(shutil.rmtree, build_directory)) return has_python_package(build_package)