Exemplo n.º 1
0
 def __init__(self, sources: List[Source],
              environment: Environment) -> None:
     """
     :param sources: a list of sources to download packages from.
     :param environment: the bound environment instance.
     """
     self.sources = [get_pypi_source()] + sources
     self.environment = environment
     self._candidate_info_cache = context.make_candidate_info_cache()
     self._hash_cache = context.make_hash_cache()
Exemplo n.º 2
0
class Config(MutableMapping):
    """A dict-like object for configuration key and values"""

    HOME_CONFIG = Path.home() / ".pdm" / "config.toml"

    pypi_url, verify_ssl = get_pypi_source()
    _config_map = {
        "cache_dir":
        ConfigItem("The root directory of cached files",
                   appdirs.user_cache_dir("pdm"), True),
        "auto_global":
        ConfigItem(
            "Use global package implicity if no local project is found",
            False,
            True,
            "PDM_AUTO_GLOBAL",
        ),
        "strategy.update":
        ConfigItem("The default strategy for updating packages", "reuse",
                   False),
        "strategy.save":
        ConfigItem("Specify how to save versions when a package is added",
                   "compatible", False),
        "parallel_install":
        ConfigItem(
            "Whether to perform installation and uninstallation in parallel",
            True,
            env_var="PDM_PARALLEL_INSTALL",
        ),
        "python.path":
        ConfigItem("The Python interpreter path", env_var="PDM_PYTHON"),
        "python.use_pyenv":
        ConfigItem("Use the pyenv interpreter", True),
        "pypi.url":
        ConfigItem(
            "The URL of PyPI mirror, defaults to https://pypi.org/simple",
            pypi_url,
            env_var="PDM_PYPI_URL",
        ),
        "pypi.verify_ssl":
        ConfigItem("Verify SSL certificate when query PyPI", verify_ssl),
        "pypi.json_api":
        ConfigItem(
            "Consult PyPI's JSON API for package metadata",
            False,
            env_var="PDM_PYPI_JSON_API",
        ),
        "use_venv":
        ConfigItem(
            "Install packages into the activated venv site packages instead of PEP 582",
            False,
            env_var="PDM_USE_VENV",
        ),
    }  # type: Dict[str, ConfigItem]
    del pypi_url, verify_ssl

    @classmethod
    def get_defaults(cls):
        return {
            k: v.default
            for k, v in cls._config_map.items() if v.should_show()
        }

    @classmethod
    def add_config(cls, name: str, item: ConfigItem) -> None:
        """Add or modify a config item"""
        cls._config_map[name] = item

    def __init__(self, config_file: Path, is_global: bool = False):
        self._data = {}
        if is_global:
            self._data.update(self.get_defaults())

        self.is_global = is_global
        self._config_file = config_file
        self._file_data = load_config(self._config_file)
        self._data.update(self._file_data)

    def _save_config(self) -> None:
        """Save the changed to config file."""
        self._config_file.parent.mkdir(parents=True, exist_ok=True)
        toml_data = {}
        for key, value in self._file_data.items():
            *parts, last = key.split(".")
            temp = toml_data
            for part in parts:
                if part not in temp:
                    temp[part] = {}
                temp = temp[part]
            temp[last] = value

        with self._config_file.open("w", encoding="utf-8") as fp:
            fp.write(tomlkit.dumps(toml_data))

    def __getitem__(self, key: str) -> Any:
        if key not in self._data:
            raise NoConfigError(key)
        env_var = self._config_map[key].env_var
        if env_var is not None and env_var in os.environ:
            env_value = os.environ[env_var]
            if isinstance(self._config_map[key].default, bool):
                env_value = ensure_boolean(env_value)
            return env_value
        return self._data[key]

    def __setitem__(self, key: str, value: Any) -> None:
        if key not in self._config_map:
            raise NoConfigError(key)
        if not self.is_global and self._config_map[key].global_only:
            raise ValueError(
                f"Config item '{key}' is not allowed to set in project config."
            )
        if isinstance(value, str):
            if value.lower() == "false":
                value = False
            elif value.lower() == "true":
                value = True
        env_var = self._config_map[key].env_var
        if env_var is not None and env_var in os.environ:
            stream.echo(
                stream.yellow(
                    "WARNING: the config is shadowed by env var '{}', "
                    "set value won't take effect.".format(env_var)))
        self._data[key] = value
        self._file_data[key] = value
        self._save_config()

    def __len__(self) -> int:
        return len(self._data)

    def __iter__(self) -> Iterable[str]:
        return iter(self._data)

    def __delitem__(self, key) -> None:
        self._data.pop(key, None)
        try:
            del self._file_data[key]
        except KeyError:
            pass
        else:
            env_var = self._config_map[key].env_var
            if env_var is not None and env_var in os.environ:
                stream.echo(
                    stream.yellow(
                        "WARNING: the config is shadowed by env var '{}', "
                        "set value won't take effect.".format(env_var)))
            self._save_config()
Exemplo n.º 3
0
class Config(MutableMapping[str, str]):
    """A dict-like object for configuration key and values"""

    pypi_url, verify_ssl = get_pypi_source()
    _config_map: Dict[str, ConfigItem] = {
        "cache_dir": ConfigItem(
            "The root directory of cached files",
            platformdirs.user_cache_dir("pdm"),
            True,
        ),
        "check_update": ConfigItem(
            "Check if there is any newer version available",
            True,
            True,
            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,
        ),
        "global_project.user_site": ConfigItem(
            "Whether to install to user site", False, True, coerce=ensure_boolean
        ),
        "project_max_depth": ConfigItem(
            "The max depth to search for a project through the parents",
            5,
            True,
            env_var="PDM_PROJECT_MAX_DEPTH",
            coerce=int,
        ),
        "strategy.update": ConfigItem(
            "The default strategy for updating packages", "reuse", False
        ),
        "strategy.save": ConfigItem(
            "Specify how to save versions when a package is added", "minimum", False
        ),
        "strategy.resolve_max_rounds": ConfigItem(
            "Specify the max rounds of resolution process",
            10000,
            env_var="PDM_RESOLVE_MAX_ROUDNS",
            coerce=int,
        ),
        "install.parallel": ConfigItem(
            "Whether to perform installation and uninstallation in parallel",
            True,
            env_var="PDM_INSTALL_PARALLEL",
            coerce=ensure_boolean,
            replace="parallel_install",
        ),
        "install.cache": ConfigItem(
            "Cache wheel installation and only put symlinks in the library root",
            False,
            coerce=ensure_boolean,
            replace="feature.install_cache",
        ),
        "install.cache_method": ConfigItem(
            "`symlink` or `pth` to create links to the cached installation",
            "symlink",
            replace="feature.install_cache_method",
        ),
        "python.path": ConfigItem("The Python interpreter path", env_var="PDM_PYTHON"),
        "python.use_pyenv": ConfigItem(
            "Use the pyenv interpreter", True, coerce=ensure_boolean
        ),
        "python.use_venv": ConfigItem(
            "Install packages into the activated venv site packages instead of PEP 582",
            False,
            env_var="PDM_USE_VENV",
            coerce=ensure_boolean,
            replace="use_venv",
        ),
        "pypi.url": ConfigItem(
            "The URL of PyPI mirror, defaults to https://pypi.org/simple",
            pypi_url,
            env_var="PDM_PYPI_URL",
        ),
        "pypi.verify_ssl": ConfigItem(
            "Verify SSL certificate when query PyPI", verify_ssl, coerce=ensure_boolean
        ),
        "pypi.json_api": ConfigItem(
            "Consult PyPI's JSON API for package metadata",
            False,
            env_var="PDM_PYPI_JSON_API",
            coerce=ensure_boolean,
        ),
    }
    del pypi_url, verify_ssl

    @classmethod
    def get_defaults(cls) -> Dict[str, Any]:
        return {k: v.default for k, v in cls._config_map.items() if v.should_show()}

    @classmethod
    def add_config(cls, name: str, item: ConfigItem) -> None:
        """Add or modify a config item"""
        cls._config_map[name] = item

    def __init__(self, config_file: Path, is_global: bool = False):
        self._data = {}
        if is_global:
            self._data.update(self.get_defaults())

        self.is_global = is_global
        self.config_file = config_file.resolve()
        self._file_data = load_config(self.config_file)
        self.deprecated = {
            v.replace: k for k, v in self._config_map.items() if v.replace
        }
        self._data.update(self._file_data)

    def _save_config(self) -> None:
        """Save the changed to config file."""
        self.config_file.parent.mkdir(parents=True, exist_ok=True)
        toml_data: Dict[str, Any] = {}
        for key, value in self._file_data.items():
            *parts, last = key.split(".")
            temp = toml_data
            for part in parts:
                if part not in temp:
                    temp[part] = {}
                temp = temp[part]
            temp[last] = value

        with self.config_file.open("w", encoding="utf-8") as fp:
            tomlkit.dump(toml_data, fp)  # type: ignore

    def __getitem__(self, key: str) -> Any:
        if key not in self._config_map and key not in self.deprecated:
            raise NoConfigError(key)
        config_key = self.deprecated.get(key, key)
        config = self._config_map[config_key]
        env_var = config.env_var
        if env_var is not None and env_var in os.environ:
            result = os.environ[env_var]
        else:
            if config_key in self._data:
                result = self._data[config_key]
            elif config.replace:
                result = self._data[config.replace]
            else:
                raise NoConfigError(key) from None
        return config.coerce(result)

    def __setitem__(self, key: str, value: Any) -> None:
        if key not in self._config_map and key not in self.deprecated:
            raise NoConfigError(key)
        config_key = self.deprecated.get(key, key)
        config = self._config_map[config_key]
        if not self.is_global and config.global_only:
            raise ValueError(
                f"Config item '{key}' is not allowed to set in project config."
            )

        value = config.coerce(value)
        env_var = config.env_var
        if env_var is not None and env_var in os.environ:
            click.echo(
                termui.yellow(
                    "WARNING: the config is shadowed by env var '{}', "
                    "the value set won't take effect.".format(env_var)
                )
            )
        self._data[config_key] = value
        self._file_data[config_key] = value
        if config.replace:
            self._data.pop(config.replace, None)
            self._file_data.pop(config.replace, None)
        self._save_config()

    def __len__(self) -> int:
        return len(self._data)

    def __iter__(self) -> Iterator[str]:
        keys: Set[str] = set()
        for key in self._data:
            if key in self._config_map:
                keys.add(key)
            elif key in self.deprecated:
                keys.add(self.deprecated[key])
        return iter(keys)

    def __delitem__(self, key: str) -> None:
        config_key = self.deprecated.get(key, key)
        config = self._config_map[config_key]
        self._data.pop(config_key, None)
        self._file_data.pop(config_key, None)
        if self.is_global and config.should_show():
            self._data[config_key] = config.default
        if config.replace:
            self._data.pop(config.replace, None)
            self._file_data.pop(config.replace, None)

        env_var = config.env_var
        if env_var is not None and env_var in os.environ:
            click.echo(
                termui.yellow(
                    "WARNING: the config is shadowed by env var '{}', "
                    "set value won't take effect.".format(env_var)
                )
            )
        self._save_config()
Exemplo n.º 4
0
class Config(MutableMapping):
    """A dict-like object for configuration key and values"""

    HOME_CONFIG = Path(appdirs.user_config_dir("pdm"))
    CONFIG_ITEMS = {
        "cache_dir": "The root directory of cached files",
        "python.path": "The Python interpreter path",
        "python.use_pyenv": "Use the pyenv interpreter",
        "pypi.url":
        "The URL of PyPI mirror, defaults to https://pypi.org/simple",
        "pypi.verify_ssl": "Verify SSL certificate when query PyPI",
    }
    DEFAULT_CONFIG = {
        "cache_dir": appdirs.user_cache_dir("pdm"),
        "python.use_pyenv": True,
    }
    DEFAULT_CONFIG.update(get_pypi_source())

    def __init__(self, project_root: Path):
        self.project_root = project_root
        self._data = self.DEFAULT_CONFIG.copy()
        self._dirty = {}

        self._project_config_file = self.project_root / ".pdm.toml"
        self._global_config_file = self.HOME_CONFIG / "config.toml"
        self._project_config = self.load_config(self._project_config_file)
        self._global_config = self.load_config(self._global_config_file)
        # First load user config, then project config
        for config in (self._global_config, self._project_config):
            self._data.update(dict(config))

    def load_config(self, file_path: Path) -> Dict[str, Any]:
        def get_item(sub_data):
            result = {}
            for k, v in sub_data.items():
                if getattr(v, "items", None) is not None:
                    result.update({
                        f"{k}.{sub_k}": sub_v
                        for sub_k, sub_v in get_item(v).items()
                    })
                else:
                    result.update({k: v})
            return result

        if not file_path.is_file():
            return {}
        return get_item(dict(tomlkit.parse(file_path.read_text("utf-8"))))

    def save_config(self, is_global: bool = False) -> None:
        data = self._global_config if is_global else self._project_config
        data.update(self._dirty)
        file_path = self._global_config_file if is_global else self._project_config_file
        file_path.parent.mkdir(exist_ok=True)
        toml_data = {}
        for key, value in data.items():
            *parts, last = key.split(".")
            temp = toml_data
            for part in parts:
                temp = temp.setdefault(part, {})
            temp[last] = value

        with file_path.open("w", encoding="utf-8") as fp:
            fp.write(tomlkit.dumps(toml_data))
        self._dirty.clear()

    def __getitem__(self, key: str) -> Any:
        try:
            return self._data[key]
        except KeyError:
            raise NoConfigError(key) from None

    def __setitem__(self, key: str, value: Any) -> None:
        if key not in self.CONFIG_ITEMS:
            raise NoConfigError(key)
        if isinstance(value, str):
            if value.lower() == "false":
                value = False
            elif value.lower() == "true":
                value = True
        self._dirty[key] = value
        self._data[key] = value

    def __len__(self) -> int:
        return len(self._data)

    def __iter__(self) -> Iterable[str]:
        return iter(self._data)

    def __delitem__(self, key) -> None:
        raise NotImplementedError
Exemplo n.º 5
0
 def sources(self) -> List[Source]:
     sources = self.tool_settings.get("source", [])
     if not any(source.get("name") == "pypi" for source in sources):
         sources.insert(0, get_pypi_source())
     return sources