def do_info( project: Project, python: bool = False, show_project: bool = False, env: bool = False, ) -> None: """Show project information.""" python_path = project.environment.python_executable python_version = get_python_version(python_path, True) if not python and not show_project and not env: rows = [ (stream.cyan("PDM version:", bold=True), project.core.version), ( stream.cyan("Python Interpreter:", bold=True), python_path + f" ({python_version})", ), (stream.cyan("Project Root:", bold=True), project.root.as_posix()), ] stream.display_columns(rows) return if python: stream.echo(python_path) if show_project: stream.echo(project.root.as_posix()) if env: stream.echo( json.dumps(project.environment.marker_environment, indent=2))
def handle(self, project: Project, options: argparse.Namespace) -> None: if project.pyproject_file.exists(): stream.echo("{}".format( stream.cyan("pyproject.toml already exists, update it now."))) else: stream.echo("{}".format( stream.cyan("Creating a pyproject.toml for PDM..."))) python = click.prompt("Please enter the Python interpreter to use", default="", show_default=False) actions.do_use(project, python) is_library = (click.prompt( "Is the project a library that will be upload to PyPI?(y/n)", default="n", ).lower() == "y") 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 handle(self, project: Project, options: argparse.Namespace) -> None: if project.pyproject_file.exists(): stream.echo("{}".format( stream.cyan("pyproject.toml already exists, update it now."))) else: stream.echo("{}".format( stream.cyan("Creating a pyproject.toml for PDM..."))) python = click.prompt("Please enter the Python interpreter to use", default="", show_default=False) actions.do_use(project, python) name = click.prompt("Project name", default=project.root.name) version = click.prompt("Project version", default="0.0.0") 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 = ".".join( map(str, get_python_version(project.environment.python_executable)[: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 stream.echo( stream.cyan("Found following files from other formats that you may import:") ) for i, (key, filepath) in enumerate(importable_files): stream.echo(f"{i}. {stream.green(filepath.as_posix())} ({key})") stream.echo( "{}. {}".format( len(importable_files), stream.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, filepath, key)
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. """ import pythonfinder python = python.strip() if python and not all(c.isdigit() for c in python.split(".")): if Path(python).exists(): python_path = find_python_in_path(python) else: python_path = shutil.which(python) if not python_path: raise NoPythonVersion(f"{python} is not a valid Python.") python_version, is_64bit = get_python_version(python_path, True) else: finder = pythonfinder.Finder() pythons = [] args = [int(v) for v in python.split(".") if v != ""] for i, entry in enumerate(finder.find_all_python_versions(*args)): python_version, is_64bit = get_python_version(entry.path.as_posix(), True) pythons.append((entry.path.as_posix(), python_version, is_64bit)) if not pythons: raise NoPythonVersion(f"Python {python} is not available on the system.") if not first and len(pythons) > 1: for i, (path, python_version, is_64bit) in enumerate(pythons): stream.echo( f"{i}. {stream.green(path)} " f"({get_python_version_string(python_version, is_64bit)})" ) selection = click.prompt( "Please select:", type=click.Choice([str(i) for i in range(len(pythons))]), default="0", show_choices=False, ) else: selection = 0 python_path, python_version, is_64bit = pythons[int(selection)] if not project.python_requires.contains(python_version): raise NoPythonVersion( "The target Python version {} doesn't satisfy " "the Python requirement: {}".format(python_version, project.python_requires) ) stream.echo( "Using Python interpreter: {} ({})".format( stream.green(python_path), get_python_version_string(python_version, is_64bit), ) ) old_path = project.config.get("python.path") new_path = python_path project.project_config["python.path"] = Path(new_path).as_posix() if old_path and Path(old_path) != Path(new_path) and not project.is_global: stream.echo(stream.cyan("Updating executable scripts...")) project.environment.update_shebangs(new_path)
def build(self, build_dir: str, **kwargs): if not os.path.exists(build_dir): os.makedirs(build_dir, exist_ok=True) stream.echo("- Building {}...".format(stream.cyan("sdist"))) version = to_filename(safe_version(self.meta.version)) target = os.path.join( build_dir, "{}-{}.tar.gz".format(self.meta.project_name, version)) tar = tarfile.open(target, mode="w:gz", format=tarfile.PAX_FORMAT) try: tar_dir = "{}-{}".format(self.meta.project_name, version) files_to_add = self.find_files_to_add(True) for relpath in files_to_add: tar.add( relpath, arcname=os.path.join(tar_dir, str(relpath)), recursive=False, ) stream.echo(f" - Adding: {relpath}", verbosity=stream.DETAIL) fd, temp_name = tempfile.mkstemp(prefix="pkg-info") pkg_info = self.format_pkginfo(False).encode("utf-8") with open(fd, "wb") as f: f.write(pkg_info) tar.add(temp_name, arcname=os.path.join(tar_dir, "PKG-INFO"), recursive=False) stream.echo(" - Adding: PKG-INFO", verbosity=stream.DETAIL) finally: tar.close() stream.echo("- Built {}".format(stream.cyan(os.path.basename(target)))) return target
def _build_pdm(self, build_dir: str, **kwargs) -> str: if not os.path.exists(build_dir): os.makedirs(build_dir, exist_ok=True) stream.echo("- Building {}...".format(stream.cyan("wheel"))) self._records.clear() fd, temp_path = tempfile.mkstemp(suffix=".whl") os.close(fd) with zipfile.ZipFile( temp_path, mode="w", compression=zipfile.ZIP_DEFLATED ) as zip_file: self._copy_module(zip_file) self._build(zip_file) self._write_metadata(zip_file) target = os.path.join(build_dir, self.wheel_filename) if os.path.exists(target): os.unlink(target) shutil.move(temp_path, target) stream.echo("- Built {}".format(stream.cyan(os.path.basename(target)))) return target
def _format_action(self, action): # 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 action_header = "%*s%-*s " % tup indent_first = 0 # long action name; start on the next line else: tup = self._current_indent, "", action_header action_header = "%*s%s\n" % tup indent_first = help_position # collect the pieces of the action help parts = [stream.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 _legacy_generate_rows(self) -> Iterator[Tuple[str, str]]: yield stream.cyan("Name:"), self._data["Name"] yield stream.cyan("Latest version:"), self._data["Version"] if self.latest_stable_version: yield (stream.cyan("Latest stable version:"), self.latest_stable_version) if self.installed_version: yield (stream.green("Installed version:"), self.installed_version) yield stream.cyan("Summary:"), self._data.get("Summary", "") yield stream.cyan("Author:"), self._data.get("Author", "") yield stream.cyan("Author email:"), self._data.get("Author-email", "") yield stream.cyan("License:"), self._data.get("License", "") yield stream.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 stream.cyan("Project URLs:"), lines[0] for line in lines[1:]: yield "", line yield stream.cyan("Platform:"), ", ".join( self._data.get("Platform", [])) yield stream.cyan("Keywords:"), ", ".join( self._data.get("Keywords", []))
def generate_rows(self) -> Iterator[Tuple[str, str]]: if self.legacy: yield from self._legacy_generate_rows() return yield stream.cyan("Name:"), self._data["name"] yield stream.cyan("Latest version:"), self._data["version"] if self.latest_stable_version: yield (stream.cyan("Latest stable version:"), self.latest_stable_version) if self.installed_version: yield (stream.green("Installed version:"), self.installed_version) yield stream.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 stream.cyan("Author:"), author_contact.get("name", "") yield stream.cyan("Author email:"), author_contact.get("email", "") yield stream.cyan("License:"), self._data.get("license", "") yield stream.cyan("Homepage:"), self._data.get("extensions", {}).get( "python.details", {}).get("project_urls", {}).get("Home", "") yield stream.cyan("Project URLs:"), self._data.get("project_url", "") yield stream.cyan("Platform:"), self._data.get("platform", "") yield stream.cyan("Keywords:"), ", ".join( self._data.get("keywords", []))