Skip to content

Commit

Permalink
Feature: Configurable global project path (#986)
Browse files Browse the repository at this point in the history
  • Loading branch information
frostming committed Mar 22, 2022
1 parent f77d896 commit 7de8c3e
Show file tree
Hide file tree
Showing 8 changed files with 53 additions and 30 deletions.
3 changes: 2 additions & 1 deletion docs/docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ The following configuration items can be retrieved and modified by `pdm config`

| Config Item | Description | Default Value | Available in Project | Env var |
| ----------------------------- | ------------------------------------------------------------------------- | ------------------------------------------------------------------------- | -------------------- | ------------------------ |
| `auto_global` | Use global package implicitly if no local project is found | `False` | No | `PDM_AUTO_GLOBAL` |
| `build_isolation` | Isolate the build environment from the project environment | Yes | True | `PDM_BUILD_ISOLATION` |
| `cache_dir` | The root directory of cached files | The default cache location on OS | No | |
| `check_update` | Check if there is any newer version available | True | No | |
| `global_project.fallback` | Use the global project implicitly if no local project is found | `False` | No | |
| `global_project.path` | The path to the global project | `~/.pdm/global-project` | No | |
| `install.cache` | Enable caching of wheel installations | False | Yes | |
| `install.cache_method` | Specify how to create links to the caches(`symlink` or `pth`) | `symlink` | Yes | |
| `install.parallel` | Whether to perform installation and uninstallation in parallel | `True` | Yes | `PDM_PARALLEL_INSTALL` |
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/usage/project.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ and it doesn't support build features. The idea is taken from Haskell's [stack](

However, unlike `stack`, by default, PDM won't use global project automatically if a local project is not found.
Users should pass `-g/--global` explicitly to activate it, since it is not very pleasing if packages go to a wrong place.
But PDM also leave the decision to users, just set the config `auto_global` to `true`.
But PDM also leave the decision to users, just set the config `global_project.fallback` to `true`.

If you want global project to track another project file other than `~/.pdm/global-project`, you can provide the
project path via `-p/--project <path>` option.
Expand Down
1 change: 1 addition & 0 deletions news/986.feature.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Make the path to the global project configurable. Rename the configuration `auto_global` to `global_project.fallback` and deprecate the old name.
19 changes: 12 additions & 7 deletions pdm/project/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,20 +86,25 @@ class Config(MutableMapping[str, str]):
True,
coerce=ensure_boolean,
),
"auto_global": ConfigItem(
"Use global package implicitly if no local project is found",
False,
True,
"PDM_AUTO_GLOBAL",
coerce=ensure_boolean,
),
"build_isolation": ConfigItem(
"Isolate build environment from the project environment",
True,
False,
"PDM_BUILD_ISOLATION",
ensure_boolean,
),
"global_project.fallback": ConfigItem(
"Use the global project implicitly if no local project is found",
False,
True,
coerce=ensure_boolean,
replace="auto_global",
),
"global_project.path": ConfigItem(
"The path to the global project",
os.path.expanduser("~/.pdm/global-project"),
True,
),
"project_max_depth": ConfigItem(
"The max depth to search for a project through the parents",
5,
Expand Down
23 changes: 17 additions & 6 deletions pdm/project/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,18 @@


class Project:
"""Core project class"""
"""Core project class.
Args:
core: The core instance.
root_path: The root path of the project.
is_global: Whether the project is global.
global_config: The path to the global config file.
"""

PYPROJECT_FILENAME = "pyproject.toml"
DEPENDENCIES_RE = re.compile(r"(?:(.+?)-)?dependencies")
LOCKFILE_VERSION = "3.1"
GLOBAL_PROJECT = Path.home() / ".pdm" / "global-project"

def __init__(
self,
Expand All @@ -71,22 +77,27 @@ def __init__(
self.core = core

if global_config is None:
global_config = self.GLOBAL_PROJECT.with_name("config.toml")
global_config = Path.home() / ".pdm/config.toml"
self.global_config = Config(Path(global_config), is_global=True)
global_project = Path(self.global_config["global_project.path"])

if root_path is None:
root_path = (
find_project_root(max_depth=self.global_config["project_max_depth"])
if not is_global
else self.GLOBAL_PROJECT
else global_project
)
if not is_global and root_path is None and self.global_config["auto_global"]:
if (
not is_global
and root_path is None
and self.global_config["global_project.fallback"]
):
self.core.ui.echo(
"Project is not found, fallback to the global project",
fg="yellow",
err=True,
)
root_path = self.GLOBAL_PROJECT
root_path = global_project
is_global = True

self.root = Path(root_path or "").absolute()
Expand Down
1 change: 1 addition & 0 deletions tests/cli/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ def test_deprecated_config_name(project, invoke):


@pytest.mark.deprecated
@pytest.mark.usefixtures("project")
def test_rename_deprected_config(tmp_path, invoke):
tmp_path.joinpath(".pdm.toml").write_text("use_venv = true\n")
with cd(tmp_path):
Expand Down
20 changes: 10 additions & 10 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
filter_requirements_with_extras,
parse_requirement,
)
from pdm.project import Project
from pdm.project.config import Config
from pdm.utils import get_finder, normalize_name
from tests import FIXTURES
Expand Down Expand Up @@ -153,13 +152,6 @@ def load_fixtures(self):
self._pypi_data = json.loads(json_file.read_text())


class TestProject(Project):
def __init__(self, core, root_path, is_global, global_config):
self.root_path = Path(root_path or ".")
self.GLOBAL_PROJECT = self.root_path / ".pdm-home" / "global-project"
super().__init__(core, root_path, is_global, global_config)


class Distribution:
def __init__(self, key, version, editable=False):
self.version = version
Expand Down Expand Up @@ -253,15 +245,23 @@ def remove_pep582_path_from_pythonpath(pythonpath):
def core():
old_config_map = Config._config_map.copy()
main = Core()
main.project_class = TestProject
yield main
# Restore the config items
Config._config_map = old_config_map


@pytest.fixture()
def project_no_init(tmp_path, mocker, core):
p = core.create_project(tmp_path)
test_home = tmp_path / ".pdm-home"
test_home.mkdir(parents=True)
test_home.joinpath("config.toml").write_text(
'[global_project]\npath = "{}"\n'.format(
test_home.joinpath("global-project").as_posix()
)
)
p = core.create_project(
tmp_path, global_config=test_home.joinpath("config.toml").as_posix()
)
mocker.patch("pdm.utils.get_finder", get_local_finder)
mocker.patch("pdm.models.environment.get_finder", get_local_finder)
tmp_path.joinpath("caches").mkdir(parents=True)
Expand Down
14 changes: 9 additions & 5 deletions tests/test_project.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,11 @@ def test_global_project(tmp_path, core):

def test_auto_global_project(tmp_path, core):
tmp_path.joinpath(".pdm-home").mkdir()
(tmp_path / ".pdm-home/config.toml").write_text("auto_global = true\n")
(tmp_path / ".pdm-home/config.toml").write_text(
"[global_project]\nfallback = true\n"
)
with cd(tmp_path):
project = core.create_project()
project = core.create_project(global_config=tmp_path / ".pdm-home/config.toml")
assert project.is_global


Expand Down Expand Up @@ -196,11 +198,13 @@ def test_select_dependencies(project):
]


def test_global_python_path_config(project_no_init):
project_no_init.root.joinpath(".pdm.toml").unlink()
def test_global_python_path_config(project_no_init, tmp_path):
tmp_path.joinpath(".pdm.toml").unlink()
project_no_init.global_config["python.path"] = sys.executable
# Recreate the project to clean cached properties
p = project_no_init.core.create_project(project_no_init.root)
p = project_no_init.core.create_project(
project_no_init.root, global_config=tmp_path / ".pdm-home/config.toml"
)
assert p.python.executable == Path(sys.executable)
assert "python.path" not in p.project_config

Expand Down

0 comments on commit 7de8c3e

Please sign in to comment.