Skip to content

Commit ff156c9

Browse files
committed
Fix search of python3 binary on macOS. Closes #247.
Allow search of multiple CLI names for a package manager.
1 parent 755cfcf commit ff156c9

7 files changed

Lines changed: 44 additions & 25 deletions

File tree

changelog.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ Changelog
99

1010
* [xbar] Add new ``Submenu layout`` boolean option in xbar plugin UI.
1111
* [xbar] Rename ``XBAR_MPM_SUBMENU`` environment variable to ``VAR_SUBMENU_lAYOUT``.
12+
* [mpm] Allow search of multiple CLI names for a package manager.
13+
* [pip] Fix search of `python3` binary on macOS. Closes #247.
1214

1315

1416
`4.0.0 (2021-04-27) <https://github.com/kdeldycke/meta-package-manager/compare/v3.6.0...v4.0.0>`_

meta_package_manager/base.py

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -116,12 +116,16 @@ def name(cls):
116116
return cls.__name__
117117

118118
@classproperty
119-
def cli_name(cls):
120-
"""Package manager's CLI name.
119+
def cli_names(cls):
120+
"""List of CLI names the package manager is known as.
121+
122+
The supported CLI names are ordered by priority. This is used for example to
123+
help out the search of the right binary in the case of the python3/python2
124+
transition.
121125
122126
Is derived by default from the manager's ID.
123127
"""
124-
return cls.id
128+
return [cls.id]
125129

126130
@classproperty
127131
def virtual(cls):
@@ -130,23 +134,31 @@ def virtual(cls):
130134
Virtual package manager are just skeleton classes used to factorize
131135
code among managers of the same family.
132136
"""
133-
return cls.__name__ == "PackageManager" or not cls.cli_name
137+
return cls.__name__ == "PackageManager" or not cls.cli_names
134138

135139
@cachedproperty
136140
def cli_path(self):
137141
"""Fully qualified path to the package manager CLI.
138142
139-
Automaticaly search the location of the CLI in the system. Only checks
140-
if the file exists. Its executability will be assessed later. See the
141-
``self.executable`` method below.
143+
Automaticaly search the location of the CLI in the system. Try multiple CLI
144+
names within several system path.
145+
146+
Only checks if the file exists. Its executability will be assessed later. See
147+
the ``self.executable`` method below.
142148
143-
Returns `None` if CLI is not found or is not a file.
149+
Returns `None` if no CLI was found or those found were not a file.
144150
"""
145151
# Check if the path exist in any of the environment locations.
146152
env_path = ":".join(self.cli_search_path + [os.getenv("PATH")])
147-
cli_path = which(self.cli_name, mode=os.F_OK, path=env_path)
153+
154+
# Search for multiple CLI names.
155+
for name in self.cli_names:
156+
cli_path = which(name, mode=os.F_OK, path=env_path)
157+
if cli_path:
158+
break
159+
logger.debug(f"{name!r} CLI not found.")
160+
148161
if not cli_path:
149-
logger.debug(f"{self.cli_name} CLI not found.")
150162
return
151163

152164
# Check if path exist and is a file.
@@ -158,7 +170,7 @@ def cli_path(self):
158170
elif not cli_path.is_file():
159171
logger.warning(f"{cli_path} is not a file.")
160172
else:
161-
logger.debug(f"CLI found at {cli_path}")
173+
logger.debug(f"{name!r} CLI found at {cli_path}")
162174

163175
return cli_path
164176

meta_package_manager/cli.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -375,7 +375,9 @@ def managers(ctx):
375375
# Build up the CLI path column content.
376376
cli_infos = "{} {}".format(
377377
OK if manager.cli_path else KO,
378-
manager.cli_path if manager.cli_path else f"{manager.cli_name!r} not found",
378+
manager.cli_path
379+
if manager.cli_path
380+
else f"no {', '.join(manager.cli_names)} found",
379381
)
380382

381383
# Build up the version column content.

meta_package_manager/managers/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ def pool():
3737
Is considered valid package manager, definitions classes which:
3838
1 - are sub-classes of PackageManager, and
3939
2 - are located in files at the same level or below this one, and
40-
3 - are not virtual (i.e. have a non null cli_name property).
40+
3 - are not virtual (i.e. have a non null `cli_names` property).
4141
4242
Returns an `OrderedDict` sorted by manager's ID.
4343
"""

meta_package_manager/managers/homebrew.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ class Homebrew(PackageManager):
4444
requirement = "2.7.0"
4545

4646
# Declare this manager as virtual, i.e. not tied to a real CLI.
47-
cli_name = None
47+
cli_names = None
4848

4949
def get_version(self):
5050
"""Fetch version from ``brew --version`` output.
@@ -472,7 +472,7 @@ def cleanup(self):
472472
class Brew(Homebrew):
473473

474474
name = "Homebrew Formulae"
475-
cli_name = "brew"
475+
cli_names = ["brew"]
476476

477477
global_args = ["--formula"]
478478

@@ -482,6 +482,6 @@ class Cask(Homebrew):
482482
# Casks are only available on macOS, not Linux.
483483
platforms = frozenset([MACOS])
484484
name = "Homebrew Cask"
485-
cli_name = "brew"
485+
cli_names = ["brew"]
486486

487487
global_args = ["--cask"]

meta_package_manager/managers/pip.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,19 +26,20 @@
2626

2727
class Pip(PackageManager):
2828

29-
"""We will use system's default Python to call out ``pip`` as a module.
29+
"""We will use the Python binary to call out ``pip`` as a module instead of a CLI.
3030
3131
This is a more robust way of managing packages: "if you're on Windows there
32-
is an added benefit to using python -m pip as it lets pip update itself."
32+
is an added benefit to using `python -m pip` as it lets `pip` update itself."
3333
Source: https://snarky.ca/why-you-should-use-python-m-pip/
3434
"""
3535

3636
platforms = frozenset([MACOS, LINUX, WINDOWS])
3737

3838
requirement = "10.0.0"
3939

40-
# We will use system's default python to call out pip as a module.
41-
cli_name = "python"
40+
# Targets `python3` CLI first to allow for some systems (like macOS) to keep the
41+
# default `python` CLI tied to the Python 2.x ecosystem.
42+
cli_names = ["python3", "python"]
4243

4344
global_args = [
4445
"-m",

meta_package_manager/tests/test_managers.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -73,13 +73,15 @@ def test_platforms():
7373
assert manager.platforms.issubset(OS_DEFINITIONS)
7474

7575

76-
def test_cli_name_type():
76+
def test_cli_names_type():
7777
"""Check the pointed CLI name and path are file-system compatible."""
7878
for manager in pool().values():
79-
assert manager.cli_name
80-
assert isinstance(manager.cli_name, str)
81-
assert manager.cli_name.isalnum()
82-
assert PurePath(manager.cli_name).name == manager.cli_name
79+
assert manager.cli_names
80+
assert isinstance(manager.cli_names, list)
81+
for name in manager.cli_names:
82+
assert isinstance(name, str)
83+
assert name.isalnum()
84+
assert PurePath(name).name == name
8385

8486

8587
def test_virtual():

0 commit comments

Comments
 (0)