Skip to content

Commit

Permalink
Merge branch 'johan/microsoft-edge' into python
Browse files Browse the repository at this point in the history
Handle paths with multiple spaces in them.

Like "Microsoft Edge Helper (GPU).app".
  • Loading branch information
walles committed Mar 10, 2024
2 parents e1669ab + 3282f71 commit ce58f72
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 96 deletions.
116 changes: 49 additions & 67 deletions px/px_commandline.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,107 +19,89 @@
PERL_BIN = re.compile("^perl[.0-9]*$")


def get_coalesce_candidate(so_far: str) -> Optional[str]:
start_index = -1
if so_far.startswith("/"):
start_index = 0
elif so_far.startswith("-"):
equals_slash_index = so_far.find("=/")
if equals_slash_index == -1:
# No =/ in the string, so we're not looking at -Djava.io.tmpdir=/tmp
return None

# Start at the slash after the equals sign
start_index = equals_slash_index + 1

if start_index == -1:
# Start not found, this is not a path
return None

colon_slash_index = so_far.rfind(":/", start_index)
if colon_slash_index == -1:
# Not a : separated path
return so_far[start_index:]

return so_far[colon_slash_index + 1 :]


def should_coalesce(
parts: List[str], exists: Callable[[str], bool] = os.path.exists
) -> Optional[bool]:
"""
Two or more (previously) space separated command line parts should be
coalesced if combining them with a space in between creates an existing file
path.
path, or a : separated series of file paths.
Return values:
* True: Coalesce
* False: Do not coalesce. The first part here does not start a coalescable sequence.
* None: Do not coalesce, but if you add more parts then that might work.
* True: Coalesce, done
* False: Do not coalesce, done
* None: Undecided, add another part and try again
"""

if parts[0].endswith("/"):
# "xxx/ yyy" would make no sense coalesced
if parts[-1].startswith("-") or parts[-1].startswith("/"):
# Last part starts a command line option or a new absolute path, don't
# coalesce.
return False

if parts[-1].startswith("-"):
# "xxx -yyy" would make no sense coalesced, that - likely means what
# comes after is a command line switch
coalesced = " ".join(parts)
candidate = get_coalesce_candidate(coalesced)
if not candidate:
# This is not a candidate for coalescing
return False

if parts[-1].startswith("/"):
# "xxx /yyy" would make no sense coalesced
return False

# Find the last possible starting point of an absolute path in part1
path_start_index = -1
if parts[0].startswith("/"):
# /x/y/z
path_start_index = 0
if (first_equals_slash := parts[0].find("=/")) >= 0:
# -Dhello=/x/y/z
path_start_index = first_equals_slash + 1
if (last_colon_slash := parts[0].rfind(":/")) >= 0:
if last_colon_slash > path_start_index:
# -Dsomepath=/a/b/c:/x/y/z
path_start_index = last_colon_slash + 1

if path_start_index == -1:
# Part 1 does not contain the start of any path, do not coalesce
return False

path_end_index_exclusive = len(parts[-1])
if (first_colon := parts[-1].find(":")) >= 0:
path_end_index_exclusive = first_colon
if (first_slash := parts[-1].find("/")) >= 0:
if first_slash < path_end_index_exclusive:
path_end_index_exclusive = first_slash

middle = " "
if len(parts) > 2:
middle = " " + " ".join(parts[1:-1]) + " "
if exists(candidate):
# Found it, done!
return True

candidate_path = (
parts[0][path_start_index:] + middle + parts[-1][:path_end_index_exclusive]
)
if exists(os.path.dirname(candidate)):
# Found the parent directory, we're on the right track, keep looking!
return None

_exists = exists(candidate_path)
if _exists:
return True
else:
if path_end_index_exclusive == len(parts[-1]):
# No hit, but no slashes in the final part, so we might still be
# inside of a multi-part name
return None
else:
# No hit, and we got something with slashes in it, so this is
# definitely a no.
return False
# Candidate does not exists, and neither does its parent directory, this is
# not it.
return False


def coalesce_count(
parts: List[str], exists: Callable[[str], bool] = os.path.exists
) -> int:
"""How many parts should be coalesced?"""

# This is what we can be sure of so far
confirmed_count = 1

section_start = 0
for coalesce_count in range(2, len(parts) + 1):
should_coalesce_ = should_coalesce(parts[section_start:coalesce_count], exists)
should_coalesce_ = should_coalesce(parts[0:coalesce_count], exists)

if should_coalesce_ is None:
# Undecided, keep looking
continue

if should_coalesce_ is False:
return confirmed_count
return 1

# should_coalesce_ is True
confirmed_count = coalesce_count

# See if the last part should also be coalesced with what comes after
# it. Think of a search path for example: "/a b:/c d"
section_start = coalesce_count - 1
return coalesce_count

# Undecided until the end, this means no coalescing should be done
return confirmed_count
return 1


def to_array(
Expand Down
82 changes: 53 additions & 29 deletions tests/px_commandline_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,41 +3,27 @@
from px import px_commandline


def test_should_coalesce():
def exists(s):
return s in [
"/Applications",
"/Applications/IntelliJ IDEA.app",
]

assert not px_commandline.should_coalesce(
["java", "-Dhello=/Applications/IntelliJ"], exists=exists
)
def test_get_coalesce_candidate():
assert px_commandline.get_coalesce_candidate("/hello") == "/hello"
assert px_commandline.get_coalesce_candidate("/hello:/baloo") == "/baloo"

assert px_commandline.should_coalesce(
["-Dhello=/Applications/IntelliJ", "IDEA.app/Contents"], exists=exists
)
assert px_commandline.get_coalesce_candidate("-Dx=/hello") == "/hello"
assert px_commandline.get_coalesce_candidate("-Dx=/hello:/baloo") == "/baloo"

assert px_commandline.should_coalesce(
[
"/Applications/IntelliJ",
"IDEA.app/Contents/plugins/maven-model/lib/maven-model.jar:/Applications/IntelliJ",
],
exists=exists,
)
assert px_commandline.get_coalesce_candidate("hello") is None
assert px_commandline.get_coalesce_candidate("hello:/baloo") is None

assert px_commandline.should_coalesce(
[
"/Applications/IntelliJ IDEA.app/Contents/plugins/maven-model/lib/maven-model.jar:/Applications/IntelliJ",
"IDEA.app/Contents/plugins/maven-server/lib/maven-server.jar",
],
exists=exists,
assert (
px_commandline.get_coalesce_candidate(
"/A/IntelliJ IDEA.app/C/p/mm/lib/mm.jar:/A/IntelliJ IDEA.app/C/p/ms/lib/ms.jar:/A/IntelliJ"
)
== "/A/IntelliJ"
)


def test_coalesce_count():
def exists(s):
return s == "/a b c"
return s in ["/", "/a b c", "/a b c/"]

assert px_commandline.coalesce_count(["/a", "b", "c"], exists=exists) == 3
assert px_commandline.coalesce_count(["/a", "b", "c/"], exists=exists) == 3
Expand Down Expand Up @@ -70,6 +56,7 @@ def test_to_array_spaced1():
in [
"/Applications",
"/Applications/IntelliJ IDEA.app",
"/Applications/IntelliJ IDEA.app/Contents",
],
) == ["java", "-Dhello=/Applications/IntelliJ IDEA.app/Contents"]

Expand All @@ -91,7 +78,10 @@ def test_to_array_spaced2():
exists=lambda s: s
in [
"/Applications",
"/Applications/IntelliJ IDEA.app",
"/Applications/IntelliJ IDEA.app/Contents/Info.plist",
"/Applications/IntelliJ IDEA.app/Contents/plugins/maven-model/lib/maven-model.jar",
"/Applications/IntelliJ IDEA.app/Contents/plugins/maven-server/lib/maven-server.jar",
"/Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven3-server-common.jar",
],
) == [
"java",
Expand Down Expand Up @@ -123,7 +113,10 @@ def test_to_array_spaced3():
exists=lambda s: s
in [
"/Applications",
"/Applications/IntelliJ IDEA CE.app",
"/Applications/IntelliJ IDEA CE.app/Contents/Info.plist",
"/Applications/IntelliJ IDEA CE.app/Contents/plugins/maven-model/lib/maven-model.jar",
"/Applications/IntelliJ IDEA CE.app/Contents/plugins/maven-server/lib/maven-server.jar",
"/Applications/IntelliJ IDEA CE.app/Contents/plugins/maven/lib/maven3-server-common.jar",
],
) == [
"java",
Expand All @@ -134,6 +127,37 @@ def test_to_array_spaced3():
]


def test_to_array_ms_edge():
complete = "/".join(
[
"/Applications",
"Microsoft Edge.app",
"Contents",
"Frameworks",
"Microsoft Edge Framework.framework",
"Versions",
"122.0.2365.63",
"Helpers",
"Microsoft Edge Helper (GPU).app",
"Contents",
"MacOS",
"Microsoft Edge Helper (GPU)",
]
)
exists = []
partial = complete
while True:
exists.append(partial)
partial = os.path.dirname(partial)
if partial == "/":
break

assert px_commandline.to_array(
complete + " --type=gpu-process",
exists=lambda s: s in exists,
) == [complete] + ["--type=gpu-process"]


def test_get_command_python():
assert px_commandline.get_command("python") == "python"
assert px_commandline.get_command("/apa/Python") == "Python"
Expand Down

0 comments on commit ce58f72

Please sign in to comment.