Skip to content
This repository has been archived by the owner on Sep 20, 2024. It is now read-only.

General: Custom function for find executable #2822

Merged
merged 7 commits into from
Mar 8, 2022
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions openpype/hosts/maya/plugins/publish/extract_look.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import sys
import json
import tempfile
import platform
import contextlib
import subprocess
from collections import OrderedDict
Expand Down Expand Up @@ -64,10 +63,6 @@ def maketx(source, destination, *args):

maketx_path = get_oiio_tools_path("maketx")

if platform.system().lower() == "windows":
# Ensure .exe extension
maketx_path += ".exe"

if not os.path.exists(maketx_path):
print(
"OIIO tool not found in {}".format(maketx_path))
Expand Down
17 changes: 9 additions & 8 deletions openpype/lib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@
site.addsitedir(python_version_dir)


from .vendor_bin_utils import (
find_executable,
get_vendor_bin_path,
get_oiio_tools_path,
get_ffmpeg_tool_path,
ffprobe_streams,
is_oiio_supported
)
from .env_tools import (
env_value_to_bool,
get_paths_from_environ,
Expand Down Expand Up @@ -48,14 +56,6 @@

from .config import get_datetime_data

from .vendor_bin_utils import (
get_vendor_bin_path,
get_oiio_tools_path,
get_ffmpeg_tool_path,
ffprobe_streams,
is_oiio_supported
)

from .python_module_tools import (
import_filepath,
modules_from_path,
Expand Down Expand Up @@ -184,6 +184,7 @@
terminal = Terminal

__all__ = [
"find_executable",
"get_openpype_execute_args",
"get_pype_execute_args",
"get_linux_launcher_args",
Expand Down
8 changes: 5 additions & 3 deletions openpype/lib/applications.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,10 @@
modules_from_path,
classes_from_module
)
from .execute import get_linux_launcher_args

from .execute import (
find_executable,
get_linux_launcher_args
)

_logger = None

Expand Down Expand Up @@ -646,7 +648,7 @@ def as_args(self):
def _realpath(self):
"""Check if path is valid executable path."""
# Check for executable in PATH
result = distutils.spawn.find_executable(self.executable_path)
result = find_executable(self.executable_path)
if result is not None:
return result

Expand Down
4 changes: 2 additions & 2 deletions openpype/lib/execute.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
import platform
import json
import tempfile
import distutils.spawn

from .log import PypeLogger as Logger
from .vendor_bin_utils import find_executable

# MSDN process creation flag (Windows only)
CREATE_NO_WINDOW = 0x08000000
Expand Down Expand Up @@ -341,7 +341,7 @@ def get_linux_launcher_args(*args):
os.path.dirname(openpype_executable),
filename
)
executable_path = distutils.spawn.find_executable(new_executable)
executable_path = find_executable(new_executable)
if executable_path is None:
return None
launch_args = [executable_path]
Expand Down
70 changes: 61 additions & 9 deletions openpype/lib/vendor_bin_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,65 @@
import json
import platform
import subprocess
import distutils

log = logging.getLogger("FFmpeg utils")
log = logging.getLogger("Vendor utils")


def find_executable(executable):
"""Find full path to executable.

Also tries additional extensions if passed executable does not contain one.

Paths where it is looked for executable is defined by 'PATH' environment
variable, 'os.confstr("CS_PATH")' or 'os.defpath'.

Args:
executable(str): Name of executable with or without extension. Can be
path to file.

Returns:
str: Full path to executable with extension (is file).
None: When the executable was not found.
"""
if os.path.isfile(executable):
return executable

low_platform = platform.system().lower()
_, ext = os.path.splitext(executable)
variants = [executable]
if not ext:
if low_platform == "windows":
exts = [".exe", ".ps1", ".bat"]
for ext in os.getenv("PATHEXT", "").split(os.pathsep):
ext = ext.lower()
if ext and ext not in exts:
exts.append(ext)
else:
exts = [".sh"]

for ext in exts:
variant = executable + ext
if os.path.isfile(variant):
return variant
variants.append(variant)

path_str = os.environ.get("PATH", None)
if path_str is None:
if hasattr(os, "confstr"):
path_str = os.confstr("CS_PATH")
elif hasattr(os, "defpath"):
path_str = os.defpath

if not path_str:
return None

paths = path_str.split(os.pathsep)
for path in paths:
for variant in variants:
filepath = os.path.abspath(os.path.join(path, executable))
if os.path.isfile(filepath):
return filepath
return None
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of curiosity. What is this logic based on? It looks like distutils.find_executable but does not look like recent code?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Source was distutils, but slightly modified. In distutils find_executable always expect ".exe" on windows, this adds other options from PATHEXT and .sh for linux and mac.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For reference sake this is what which does in Avalon core which looks somewhat similar

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Handle less cases. When passed executable is existing path it should just return it. If the passed executable is path but don't have extension then this function will just add extension and return it. Then will start to look into PATH for possible variants.
Adding the os.access(filepath, os.X_OK) seems like a good idea.



def get_vendor_bin_path(bin_app):
Expand Down Expand Up @@ -41,11 +97,7 @@ def get_oiio_tools_path(tool="oiiotool"):
Default is "oiiotool".
"""
oiio_dir = get_vendor_bin_path("oiio")
if platform.system().lower() == "windows" and not tool.lower().endswith(
".exe"
):
tool = "{}.exe".format(tool)
return os.path.join(oiio_dir, tool)
return find_executable(os.path.join(oiio_dir, tool))


def get_ffmpeg_tool_path(tool="ffmpeg"):
Expand All @@ -61,7 +113,7 @@ def get_ffmpeg_tool_path(tool="ffmpeg"):
ffmpeg_dir = get_vendor_bin_path("ffmpeg")
if platform.system().lower() == "windows":
ffmpeg_dir = os.path.join(ffmpeg_dir, "bin")
return os.path.join(ffmpeg_dir, tool)
return find_executable(os.path.join(ffmpeg_dir, tool))


def ffprobe_streams(path_to_file, logger=None):
Expand Down Expand Up @@ -122,7 +174,7 @@ def is_oiio_supported():
"""
loaded_path = oiio_path = get_oiio_tools_path()
if oiio_path:
oiio_path = distutils.spawn.find_executable(oiio_path)
oiio_path = find_executable(oiio_path)

if not oiio_path:
log.debug("OIIOTool is not configured or not present at {}".format(
Expand Down