def do_info( project: Project, python: bool = False, show_project: bool = False, env: bool = False, ) -> None: """Show project information.""" check_project_file(project) python_path = project.python_executable python_version, is_64bit = get_python_version(python_path, True) if not python and not show_project and not env: rows = [ (termui.cyan("PDM version:", bold=True), project.core.version), ( termui.cyan("Python Interpreter:", bold=True), python_path + f" ({get_python_version_string(python_version, is_64bit)})", ), (termui.cyan("Project Root:", bold=True), project.root.as_posix()), ] project.core.ui.display_columns(rows) return if python: project.core.ui.echo(python_path) if show_project: project.core.ui.echo(project.root.as_posix()) if env: project.core.ui.echo( json.dumps(project.environment.marker_environment, indent=2))
def handle(self, project: Project, options: argparse.Namespace) -> None: check_project_file(project) interpreter = project.python if options.python: project.core.ui.echo(str(interpreter.executable)) elif options.where: project.core.ui.echo(str(project.root)) elif options.packages: project.core.ui.echo(str(project.environment.packages_path)) elif options.env: project.core.ui.echo( json.dumps(project.environment.marker_environment, indent=2)) else: rows = [ (termui.cyan("PDM version:", bold=True), project.core.version), ( termui.cyan("Python Interpreter:", bold=True), f"{interpreter.executable} ({interpreter.identifier})", ), (termui.cyan("Project Root:", bold=True), project.root.as_posix()), ( termui.cyan("Project Packages:", bold=True), str(project.environment.packages_path), ), ] project.core.ui.display_columns(rows)
def handle(self, project: Project, options: argparse.Namespace) -> None: if project.pyproject_file.exists(): project.core.ui.echo("{}".format( termui.cyan("pyproject.toml already exists, update it now."))) else: project.core.ui.echo("{}".format( termui.cyan("Creating a pyproject.toml for PDM..."))) non_interactive = options.non_interactive if non_interactive: actions.do_use(project, "3", True) else: actions.do_use(project) is_library = (False if non_interactive else click.confirm( "Is the project a library that will be upload to PyPI?", )) if is_library: name = self.ask("Project name", project.root.name, non_interactive) version = self.ask("Project version", "0.1.0", non_interactive) else: name, version = "", "" license = self.ask("License(SPDX name)", "MIT", non_interactive) git_user, git_email = get_user_email_from_git() author = self.ask("Author name", git_user, non_interactive) email = self.ask("Author email", git_email, non_interactive) python_version = f"{project.python.major}.{project.python.minor}" python_requires = self.ask("Python requires('*' to allow any)", f">={python_version}", non_interactive) actions.do_init(project, name, version, license, author, email, python_requires) if not non_interactive: actions.ask_for_import(project)
def handle(self, project: Project, options: argparse.Namespace) -> None: if project.pyproject_file.exists(): project.core.ui.echo("{}".format( termui.cyan("pyproject.toml already exists, update it now."))) else: project.core.ui.echo("{}".format( termui.cyan("Creating a pyproject.toml for PDM..."))) actions.do_use(project) is_library = click.confirm( "Is the project a library that will be upload to PyPI?", ) if is_library: name = click.prompt("Project name", default=project.root.name) version = click.prompt("Project version", default="0.1.0") else: name, version = "", "" license = click.prompt("License(SPDX name)", default="MIT") git_user, git_email = get_user_email_from_git() author = click.prompt("Author name", default=git_user) email = click.prompt("Author email", default=git_email) python_version, _ = get_python_version(project.python_executable, True, 2) python_requires = click.prompt("Python requires('*' to allow any)", default=f">={python_version}") actions.do_init(project, name, version, license, author, email, python_requires) actions.ask_for_import(project)
def ask_for_import(project: Project) -> None: """Show possible importable files and ask user to decide""" importable_files = list(find_importable_files(project)) if not importable_files: return project.core.ui.echo( termui.cyan("Found following files from other formats that you may import:") ) for i, (key, filepath) in enumerate(importable_files): project.core.ui.echo(f"{i}. {termui.green(filepath.as_posix())} ({key})") project.core.ui.echo( "{}. {}".format( len(importable_files), termui.yellow("don't do anything, I will import later."), ) ) choice = click.prompt( "Please select:", type=click.Choice([str(i) for i in range(len(importable_files) + 1)]), show_default=False, ) if int(choice) == len(importable_files): return key, filepath = importable_files[int(choice)] do_import(project, str(filepath), key)
def do_use(project: Project, python: Optional[str] = "", first: Optional[bool] = False) -> None: """Use the specified python version and save in project config. The python can be a version string or interpreter path. """ def version_matcher(py_version): return project.python_requires.contains(str(py_version.version)) python = python.strip() found_interpreters = list( dict.fromkeys( filter(version_matcher, project.find_interpreters(python)))) if not found_interpreters: raise NoPythonVersion("Python interpreter is not found on the system.") if first or len(found_interpreters) == 1: selected_python = found_interpreters[0] else: project.core.ui.echo("Please enter the Python interpreter to use") for i, py_version in enumerate(found_interpreters): python_version = str(py_version.version) is_64bit = py_version.get_architecture() == "64bit" version_string = get_python_version_string(python_version, is_64bit) project.core.ui.echo( f"{i}. {termui.green(py_version.executable)} ({version_string})" ) selection = click.prompt( "Please select:", type=click.Choice([str(i) for i in range(len(found_interpreters))]), default="0", show_choices=False, ) selected_python = found_interpreters[int(selection)] old_path = project.config.get("python.path") new_path = selected_python.executable python_version = str(selected_python.version) is_64bit = selected_python.get_architecture() == "64bit" project.core.ui.echo("Using Python interpreter: {} ({})".format( termui.green(str(new_path)), get_python_version_string(python_version, is_64bit), )) project.python_executable = new_path if old_path and Path(old_path) != Path(new_path) and not project.is_global: project.core.ui.echo(termui.cyan("Updating executable scripts...")) project.environment.update_shebangs(new_path)
def _format_action(self, action: Action) -> str: # determine the required width and the entry label help_position = min(self._action_max_length + 2, self._max_help_position) help_width = max(self._width - help_position, 11) action_width = help_position - self._current_indent - 2 action_header = self._format_action_invocation(action) # no help; start on same line and add a final newline if not action.help: tup = self._current_indent, "", action_header action_header = "%*s%s\n" % tup # short action name; start on the same line and pad two spaces elif len(action_header) <= action_width: tup = self._current_indent, "", action_width, action_header # type: ignore action_header = "%*s%-*s " % tup # type: ignore indent_first = 0 # long action name; start on the next line else: tup = self._current_indent, "", action_header # type: ignore action_header = "%*s%s\n" % tup indent_first = help_position # collect the pieces of the action help parts = [termui.cyan(action_header)] # if there was help for the action, add lines of help text if action.help: help_text = self._expand_help(action) help_lines = self._split_lines(help_text, help_width) parts.append("%*s%s\n" % (indent_first, "", help_lines[0])) for line in help_lines[1:]: parts.append("%*s%s\n" % (help_position, "", line)) # or add a newline if the description doesn't end with one elif not action_header.endswith("\n"): parts.append("\n") # if there are any sub-actions, add their help as well for subaction in self._iter_indented_subactions(action): parts.append(self._format_action(subaction)) # return a single string return self._join_parts(parts)
def do_use(project: Project, python: str = "", first: bool = False) -> None: """Use the specified python version and save in project config. The python can be a version string or interpreter path. """ def version_matcher(py_version: PythonInfo) -> bool: return project.python_requires.contains(str(py_version.version)) if python: python = python.strip() found_interpreters = list( dict.fromkeys( filter(version_matcher, project.find_interpreters(python)))) if not found_interpreters: raise NoPythonVersion("Python interpreter is not found on the system.") if first or len(found_interpreters) == 1: selected_python = found_interpreters[0] else: project.core.ui.echo("Please enter the Python interpreter to use") for i, py_version in enumerate(found_interpreters): project.core.ui.echo( f"{i}. {termui.green(py_version.executable)} ({py_version.identifier})" ) selection = click.prompt( "Please select:", type=click.Choice([str(i) for i in range(len(found_interpreters))]), default="0", show_choices=False, ) selected_python = found_interpreters[int(selection)] old_path = project.python.executable if "python.path" in project.config else None new_path = selected_python.executable project.core.ui.echo("Using Python interpreter: {} ({})".format( termui.green(str(new_path)), selected_python.identifier, )) project.python = selected_python if (old_path and Path(old_path) != Path(new_path) and not project.environment.is_global): project.core.ui.echo(termui.cyan("Updating executable scripts...")) project.environment.update_shebangs(old_path, new_path)
def do_use( project: Project, python: str = "", first: bool = False, ignore_remembered: bool = False, ) -> None: """Use the specified python version and save in project config. The python can be a version string or interpreter path. """ if python: python = python.strip() def version_matcher(py_version: PythonInfo) -> bool: return project.python_requires.contains(str(py_version.version), True) if not project.cache_dir.exists(): project.cache_dir.mkdir(parents=True) use_cache: JSONFileCache[str, str] = JSONFileCache( project.cache_dir / "use_cache.json" ) selected_python: PythonInfo | None = None if python and not ignore_remembered: if use_cache.has_key(python): path = use_cache.get(python) cached_python = PythonInfo.from_path(path) if not cached_python.valid: project.core.ui.echo( f"The last selection is corrupted. {path!r}", fg="red", err=True, ) elif version_matcher(cached_python): project.core.ui.echo( "Using the last selection, add '-i' to ignore it.", fg="yellow", err=True, ) selected_python = cached_python if selected_python is None: found_interpreters = list(dict.fromkeys(project.find_interpreters(python))) matching_interperters = list(filter(version_matcher, found_interpreters)) if not found_interpreters: raise NoPythonVersion("Python interpreter is not found on the system.") if not matching_interperters: project.core.ui.echo("Interpreters found but not matching:", err=True) for py in found_interpreters: project.core.ui.echo(f" - {py.executable} ({py.identifier})", err=True) raise NoPythonVersion( "No python is found meeting the requirement " f"{termui.green('python' + str(project.python_requires))}" ) if first or len(matching_interperters) == 1: selected_python = matching_interperters[0] else: project.core.ui.echo("Please enter the Python interpreter to use") for i, py_version in enumerate(matching_interperters): project.core.ui.echo( f"{i}. {termui.green(str(py_version.executable))} " f"({py_version.identifier})" ) selection = click.prompt( "Please select:", type=click.Choice([str(i) for i in range(len(matching_interperters))]), default="0", show_choices=False, ) selected_python = matching_interperters[int(selection)] if python: use_cache.set(python, selected_python.path.as_posix()) if not selected_python.valid: path = str(selected_python.executable) raise InvalidPyVersion(f"Invalid Python interpreter: {path}") old_python = project.python if "python.path" in project.config else None project.core.ui.echo( "Using Python interpreter: {} ({})".format( termui.green(str(selected_python.executable)), selected_python.identifier, ) ) project.python = selected_python if ( old_python and old_python.path != selected_python.path and not project.environment.is_global ): project.core.ui.echo(termui.cyan("Updating executable scripts...")) project.environment.update_shebangs(selected_python.executable.as_posix())
def generate_rows(self) -> Iterator[tuple[str, str]]: yield termui.cyan("Name:"), self._parsed["name"] yield termui.cyan("Latest version:"), self._parsed["version"] if self.latest_stable_version: yield (termui.cyan("Latest stable version:"), self.latest_stable_version) if self.installed_version: yield (termui.green("Installed version:"), self.installed_version) yield termui.cyan("Summary:"), self._parsed.get("summary", "") yield termui.cyan("Requires Python:"), self._parsed["requires-python"] yield termui.cyan("Author:"), self._parsed.get("author", "") yield termui.cyan("Author email:"), self._parsed.get("email", "") yield termui.cyan("License:"), self._parsed.get("license", "") yield termui.cyan("Homepage:"), self._parsed.get("homepage", "") yield from itertools.zip_longest( (termui.cyan("Project URLs:"), ), self._parsed.get("project-urls", []), fillvalue="", ) yield termui.cyan("Platform:"), self._parsed.get("platform", "") yield termui.cyan("Keywords:"), self._parsed.get("keywords", "")
def _legacy_generate_rows(self) -> Iterator[Tuple[str, str]]: yield termui.cyan("Name:"), self._data["Name"] yield termui.cyan("Latest version:"), self._data["Version"] if self.latest_stable_version: yield (termui.cyan("Latest stable version:"), self.latest_stable_version) if self.installed_version: yield (termui.green("Installed version:"), self.installed_version) yield termui.cyan("Summary:"), self._data.get("Summary", "") yield termui.cyan("Author:"), self._data.get("Author", "") yield termui.cyan("Author email:"), self._data.get("Author-email", "") yield termui.cyan("License:"), self._data.get("License", "") yield termui.cyan("Requires python:"), self._data.get( "Requires-Python", "") yield termui.cyan("Platform:"), ", ".join( self._data.get("Platform", [])) yield termui.cyan("Keywords:"), ", ".join( self._data.get("Keywords", [])) yield termui.cyan("Homepage:"), self._data.get("Home-page", "") if self._data.get("Project-URL"): lines = [ ":".join(parts) for parts in self._data.get("Project-URL") ] yield termui.cyan("Project URLs:"), lines[0] for line in lines[1:]: yield "", line
def generate_rows(self) -> Iterator[Tuple[str, str]]: if self.legacy: yield from self._legacy_generate_rows() return yield termui.cyan("Name:"), self._data["name"] yield termui.cyan("Latest version:"), self._data["version"] if self.latest_stable_version: yield (termui.cyan("Latest stable version:"), self.latest_stable_version) if self.installed_version: yield (termui.green("Installed version:"), self.installed_version) yield termui.cyan("Summary:"), self._data.get("summary", "") contacts = (self._data.get("extensions", {}).get("python.details", {}).get("contacts")) if contacts: author_contact = next( iter(c for c in contacts if c["role"] == "author"), {}) yield termui.cyan("Author:"), author_contact.get("name", "") yield termui.cyan("Author email:"), author_contact.get("email", "") yield termui.cyan("License:"), self._data.get("license", "") yield termui.cyan("Homepage:"), self._data.get("extensions", {}).get( "python.details", {}).get("project_urls", {}).get("Home", "") yield termui.cyan("Project URLs:"), self._data.get("project_url", "") yield termui.cyan("Platform:"), self._data.get("platform", "") yield termui.cyan("Keywords:"), ", ".join( self._data.get("keywords", []))