def initialize(self, i, o): from poetry.semver import parse_constraint from poetry.utils.env import Env super(EnvCommand, self).initialize(i, o) # Checking compatibility of the current environment with # the python dependency specified in pyproject.toml current_env = Env.get() supported_python = self.poetry.package.python_constraint current_python = parse_constraint(".".join( str(v) for v in current_env.version_info[:3])) if not supported_python.allows(current_python): raise RuntimeError( "The current Python version ({}) is not supported by the project ({})\n" "Please activate a compatible Python version.".format( current_python, self.poetry.package.python_versions)) self._env = Env.create_venv(o, self.poetry.package.name, cwd=self.poetry.file.parent) if self._env.is_venv() and o.is_verbose(): o.writeln("Using virtualenv: <comment>{}</>".format( self._env.path))
def test_env_get_in_project_venv(tmp_dir, environ): if "VIRTUAL_ENV" in environ: del environ["VIRTUAL_ENV"] (Path(tmp_dir) / ".venv").mkdir() venv = Env.get(cwd=Path(tmp_dir)) assert venv.path == Path(tmp_dir) / ".venv"
def handle(self): from poetry.layouts import layout from poetry.utils._compat import Path from poetry.utils.env import Env from poetry.vcs.git import GitConfig if self.option("src"): layout_ = layout("src") else: layout_ = layout("standard") path = Path.cwd() / Path(self.argument("path")) name = self.option("name") if not name: name = path.name if path.exists(): if list(path.glob("*")): # Directory is not empty. Aborting. raise RuntimeError("Destination <fg=yellow>{}</> " "exists and is not empty".format(path)) readme_format = "rst" config = GitConfig() author = None if config.get("user.name"): author = config["user.name"] author_email = config.get("user.email") if author_email: author += " <{}>".format(author_email) current_env = Env.get() default_python = "^{}".format(".".join( str(v) for v in current_env.version_info[:2])) layout_ = layout_( name, "0.1.0", author=author, readme_format=readme_format, python=default_python, ) layout_.create(path) self.line("Created package <info>{}</> in <fg=blue>{}</>".format( name, path.relative_to(Path.cwd())))
def handle(self): from poetry.layouts import layout from poetry.utils._compat import Path from poetry.utils.env import Env from poetry.vcs.git import GitConfig if (Path.cwd() / "pyproject.toml").exists(): self.error("A pyproject.toml file already exists.") return 1 vcs_config = GitConfig() self.line([ "", "This command will guide you through creating your <info>pyproject.toml</> config.", "", ]) name = self.option("name") if not name: name = Path.cwd().name.lower() question = self.create_question( "Package name [<comment>{}</comment>]: ".format(name), default=name) name = self.ask(question) version = "0.1.0" question = self.create_question( "Version [<comment>{}</comment>]: ".format(version), default=version) version = self.ask(question) description = self.option("description") or "" question = self.create_question( "Description [<comment>{}</comment>]: ".format(description), default=description, ) description = self.ask(question) author = self.option("author") if not author and vcs_config and vcs_config.get("user.name"): author = vcs_config["user.name"] author_email = vcs_config.get("user.email") if author_email: author += " <{}>".format(author_email) question = self.create_question( "Author [<comment>{}</comment>, n to skip]: ".format(author), default=author) question.validator = lambda v: self._validate_author(v, author) author = self.ask(question) if not author: authors = [] else: authors = [author] license = self.option("license") or "" question = self.create_question( "License [<comment>{}</comment>]: ".format(license), default=license) question.validator = self._validate_license license = self.ask(question) current_env = Env.get() default_python = "^{}".format(".".join( str(v) for v in current_env.version_info[:2])) question = self.create_question( "Compatible Python versions [<comment>{}</comment>]: ".format( default_python), default=default_python, ) python = self.ask(question) self.line("") requirements = {} question = ("Would you like to define your dependencies" " (require) interactively?") if self.confirm(question, True): requirements = self._format_requirements( self._determine_requirements(self.option("dependency"))) dev_requirements = {} question = ("Would you like to define your dev dependencies" " (require-dev) interactively") if self.confirm(question, True): dev_requirements = self._format_requirements( self._determine_requirements(self.option("dev-dependency"))) layout_ = layout("standard")( name, version, description=description, author=authors[0] if authors else None, license=license, python=python, dependencies=requirements, dev_dependencies=dev_requirements, ) content = layout_.generate_poetry_content() if self.input.is_interactive(): self.line("<info>Generated file</info>") self.line(["", content, ""]) if not self.confirm("Do you confirm generation?", True): self.line("<error>Command aborted</error>") return 1 with (Path.cwd() / "pyproject.toml").open("w") as f: f.write(content)
def handle(self): from poetry.packages import ProjectPackage from poetry.puzzle import Solver from poetry.repositories.repository import Repository from poetry.semver import parse_constraint from poetry.utils.env import Env packages = self.argument("package") if not packages: package = self.poetry.package else: package = ProjectPackage(self.poetry.package.name, self.poetry.package.version) requirements = self._format_requirements(packages) for name, constraint in requirements.items(): dep = package.add_dependency(name, constraint) extras = [] for extra in self.option("extras"): if " " in extra: extras += [e.strip() for e in extra.split(" ")] else: extras.append(extra) for ex in extras: dep.extras.append(ex) package.python_versions = self.option("python") or ( self.poetry.package.python_versions) pool = self.poetry.pool solver = Solver(package, pool, Repository(), Repository(), self.output) ops = solver.solve() self.line("") self.line("Resolution results:") self.line("") if self.option("tree"): show_command = self.get_application().find("show") show_command.output = self.output show_command.init_styles() packages = [op.package for op in ops] repo = Repository(packages) requires = package.requires + package.dev_requires for pkg in repo.packages: for require in requires: if pkg.name == require.name: show_command.display_package_tree(pkg, repo) break return 0 env = Env.get() current_python_version = parse_constraint(".".join( str(v) for v in env.version_info)) for op in ops: pkg = op.package if self.option("install"): if not pkg.python_constraint.allows( current_python_version) or not env.is_valid_for_marker( pkg.marker): continue self.line(" - <info>{}</info> (<comment>{}</comment>)".format( pkg.name, pkg.version)) if not pkg.python_constraint.is_any(): self.line(" - python: {}".format(pkg.python_versions)) if not pkg.marker.is_any(): self.line(" - marker: {}".format(pkg.marker))
def search_for_directory( self, dependency): # type: (DirectoryDependency) -> List[Package] if dependency.supports_poetry(): from poetry.poetry import Poetry poetry = Poetry.create(dependency.full_path) pkg = poetry.package package = Package(pkg.name, pkg.version) for dep in pkg.requires: if not dep.is_optional(): package.requires.append(dep) for extra, deps in pkg.extras.items(): if extra not in package.extras: package.extras[extra] = [] for dep in deps: package.extras[extra].append(dep) package.python_versions = pkg.python_versions else: # Execute egg_info current_dir = os.getcwd() os.chdir(str(dependency.full_path)) try: cwd = dependency.full_path venv = Env.get(NullIO(), cwd=cwd) venv.run("python", "setup.py", "egg_info") except EnvCommandError: result = SetupReader.read_from_directory(dependency.full_path) if not result["name"]: # The name could not be determined # We use the dependency name result["name"] = dependency.name if not result["version"]: # The version could not be determined # so we raise an error since it is mandatory raise RuntimeError( "Unable to retrieve the package version for {}".format( dependency.path)) package_name = result["name"] package_version = result["version"] python_requires = result["python_requires"] if python_requires is None: python_requires = "*" package_summary = "" requires = "" for dep in result["install_requires"]: requires += dep + "\n" if result["extras_require"]: requires += "\n" for extra_name, deps in result["extras_require"].items(): requires += "[{}]\n".format(extra_name) for dep in deps: requires += dep + "\n" requires += "\n" reqs = parse_requires(requires) else: os.chdir(current_dir) # Sometimes pathlib will fail on recursive # symbolic links, so we need to workaround it # and use the glob module instead. # Note that this does not happen with pathlib2 # so it's safe to use it for Python < 3.4. if PY35: egg_info = next( Path(p) for p in glob.glob( os.path.join(str(dependency.full_path), "**", "*.egg-info"), recursive=True, )) else: egg_info = next(dependency.full_path.glob("**/*.egg-info")) meta = pkginfo.UnpackedSDist(str(egg_info)) package_name = meta.name package_version = meta.version package_summary = meta.summary python_requires = meta.requires_python if meta.requires_dist: reqs = list(meta.requires_dist) else: reqs = [] requires = egg_info / "requires.txt" if requires.exists(): with requires.open() as f: reqs = parse_requires(f.read()) finally: os.chdir(current_dir) package = Package(package_name, package_version) if dependency.name != package.name: # For now, the dependency's name must match the actual package's name raise RuntimeError( "The dependency name for {} does not match the actual package's name: {}" .format(dependency.name, package.name)) package.description = package_summary for req in reqs: dep = dependency_from_pep_508(req) if dep.in_extras: for extra in dep.in_extras: if extra not in package.extras: package.extras[extra] = [] package.extras[extra].append(dep) if not dep.is_optional(): package.requires.append(dep) if python_requires: package.python_versions = python_requires package.source_type = "directory" package.source_url = dependency.path.as_posix() for extra in dependency.extras: if extra in package.extras: for dep in package.extras[extra]: dep.activate() package.requires += package.extras[extra] return [package]
def __init__( self, path, # type: Path category="main", # type: str optional=False, # type: bool base=None, # type: Path develop=True, # type: bool ): from . import dependency_from_pep_508 from .package import Package self._path = path self._base = base self._full_path = path self._develop = develop self._supports_poetry = False if self._base and not self._path.is_absolute(): self._full_path = self._base / self._path if not self._full_path.exists(): raise ValueError("Directory {} does not exist".format(self._path)) if self._full_path.is_file(): raise ValueError("{} is a file, expected a directory".format( self._path)) # Checking content to dertermine actions setup = self._full_path / "setup.py" pyproject = TomlFile(self._full_path / "pyproject.toml") if pyproject.exists(): pyproject_content = pyproject.read() self._supports_poetry = ("tool" in pyproject_content and "poetry" in pyproject_content["tool"]) if not setup.exists() and not self._supports_poetry: raise ValueError( "Directory {} does not seem to be a Python package".format( self._full_path)) if self._supports_poetry: from poetry.poetry import Poetry poetry = Poetry.create(self._full_path) package = poetry.package self._package = Package(package.pretty_name, package.version) self._package.requires += package.requires self._package.dev_requires += package.dev_requires self._package.extras = package.extras self._package.python_versions = package.python_versions else: # Execute egg_info current_dir = os.getcwd() os.chdir(str(self._full_path)) try: cwd = base venv = Env.get(NullIO(), cwd=cwd) venv.run("python", "setup.py", "egg_info") except EnvCommandError: result = SetupReader.read_from_directory(self._full_path) if not result["name"]: # The name could not be determined # so we raise an error since it is mandatory raise RuntimeError( "Unable to retrieve the package name for {}".format( path)) if not result["version"]: # The version could not be determined # so we raise an error since it is mandatory raise RuntimeError( "Unable to retrieve the package version for {}".format( path)) package_name = result["name"] package_version = result["version"] python_requires = result["python_requires"] if python_requires is None: python_requires = "*" package_summary = "" requires = "" for dep in result["install_requires"]: requires += dep + "\n" if result["extras_require"]: requires += "\n" for extra_name, deps in result["extras_require"].items(): requires += "[{}]\n".format(extra_name) for dep in deps: requires += dep + "\n" requires += "\n" reqs = parse_requires(requires) else: os.chdir(current_dir) # Sometimes pathlib will fail on recursive # symbolic links, so we need to workaround it # and use the glob module instead. # Note that this does not happen with pathlib2 # so it's safe to use it for Python < 3.4. if PY35: egg_info = next( Path(p) for p in glob.glob( os.path.join(str(self._full_path), "**", "*.egg-info"), recursive=True, )) else: egg_info = next(self._full_path.glob("**/*.egg-info")) meta = pkginfo.UnpackedSDist(str(egg_info)) package_name = meta.name package_version = meta.version package_summary = meta.summary python_requires = meta.requires_python if meta.requires_dist: reqs = list(meta.requires_dist) else: reqs = [] requires = egg_info / "requires.txt" if requires.exists(): with requires.open() as f: reqs = parse_requires(f.read()) finally: os.chdir(current_dir) package = Package(package_name, package_version) package.description = package_summary for req in reqs: package.requires.append(dependency_from_pep_508(req)) if python_requires: package.python_versions = python_requires self._package = package self._package.source_type = "directory" self._package.source_url = self._path.as_posix() super(DirectoryDependency, self).__init__( self._package.name, self._package.version, category=category, optional=optional, allows_prereleases=True, )
def search_for_vcs(self, dependency): # type: (VCSDependency) -> List[Package] """ Search for the specifications that match the given VCS dependency. Basically, we clone the repository in a temporary directory and get the information we need by checking out the specified reference. """ if dependency.vcs != "git": raise ValueError("Unsupported VCS dependency {}".format( dependency.vcs)) tmp_dir = Path( mkdtemp(prefix="pypoetry-git-{}".format(dependency.name))) try: git = Git() git.clone(dependency.source, tmp_dir) git.checkout(dependency.reference, tmp_dir) revision = git.rev_parse(dependency.reference, tmp_dir).strip() if dependency.tag or dependency.rev: revision = dependency.reference pyproject = TomlFile(tmp_dir / "pyproject.toml") pyproject_content = None has_poetry = False if pyproject.exists(): pyproject_content = pyproject.read() has_poetry = ("tool" in pyproject_content and "poetry" in pyproject_content["tool"]) if pyproject_content and has_poetry: # If a pyproject.toml file exists # We use it to get the information we need info = pyproject_content["tool"]["poetry"] name = info["name"] version = info["version"] package = Package(name, version, version) package.source_type = dependency.vcs package.source_url = dependency.source package.source_reference = dependency.reference for req_name, req_constraint in info["dependencies"].items(): if req_name == "python": package.python_versions = req_constraint continue package.add_dependency(req_name, req_constraint) else: # We need to use setup.py here # to figure the information we need # We need to place ourselves in the proper # folder for it to work venv = Env.get(self._io) current_dir = os.getcwd() os.chdir(tmp_dir.as_posix()) try: try: venv.run("python", "setup.py", "egg_info") except EnvCommandError: # Most likely an error with the egg_info command self.debug( "<warning>Error executing the egg_info command. Reading setup files.</warning>" ) result = SetupReader.read_from_directory(tmp_dir) if not result["name"]: result["name"] = dependency.name if not result["version"]: # The version could not be determined # so we raise an error since it is mandatory raise RuntimeError( "Unable to retrieve the version for {}".format( dependency.name)) package_name = result["name"] package_version = result["version"] python_requires = result["python_requires"] requires = "" for dep in result["install_requires"]: requires += dep + "\n" if result["extras_require"]: requires += "\n" for extra_name, deps in result["extras_require"].items( ): requires += "[{}]\n".format(extra_name) for dep in deps: requires += dep + "\n" requires += "\n" reqs = parse_requires(requires) else: # Sometimes pathlib will fail on recursive # symbolic links, so we need to workaround it # and use the glob module instead. # Note that this does not happen with pathlib2 # so it's safe to use it for Python < 3.4. if PY35: egg_info = next( Path(p) for p in glob.glob( os.path.join(str(tmp_dir), "**", "*.egg-info"), recursive=True, )) else: egg_info = next(tmp_dir.glob("**/*.egg-info")) meta = pkginfo.UnpackedSDist(str(egg_info)) package_name = meta.name package_version = meta.version python_requires = meta.requires_python if meta.requires_dist: reqs = list(meta.requires_dist) else: reqs = [] requires = egg_info / "requires.txt" if requires.exists(): with requires.open() as f: reqs = parse_requires(f.read()) package = Package(package_name, package_version) if python_requires: package.python_versions = python_requires for req in reqs: dep = dependency_from_pep_508(req) if dep.in_extras: for extra in dep.in_extras: if extra not in package.extras: package.extras[extra] = [] package.extras[extra].append(dep) package.requires.append(dep) except Exception: raise finally: os.chdir(current_dir) package.source_type = "git" package.source_url = dependency.source package.source_reference = revision except Exception: raise finally: shutil.rmtree(tmp_dir.as_posix()) if dependency.name != package.name: # For now, the dependency's name must match the actual package's name raise RuntimeError( "The dependency name for {} does not match the actual package's name: {}" .format(dependency.name, package.name)) if dependency.extras: for extra in dependency.extras: if extra in package.extras: for dep in package.extras[extra]: dep.activate() return [package]