コード例 #1
0
ファイル: test_api.py プロジェクト: skeptycal/cleo
    def test_option(self):
        validator = Integer()
        opt = option(
            'foo', 'f', 'The foo option.',
            flag=True,
            validator=validator
        )

        self.assertIsInstance(opt, InputOption)
        self.assertEqual('foo', opt.get_name())
        self.assertEqual('The foo option.', opt.get_description())
        self.assertTrue(opt.is_flag())
        self.assertEqual(validator, opt.get_validator())

        opt = option(
            'foo', 'f', 'The foo option.',
            value_required=True,
            is_list=True,
            default=['default'],
            validator=validator
        )

        self.assertIsInstance(opt, InputOption)
        self.assertEqual('foo', opt.get_name())
        self.assertEqual('The foo option.', opt.get_description())
        self.assertFalse(opt.is_flag())
        self.assertTrue(opt.is_value_required())
        self.assertTrue(opt.is_list())
        self.assertEqual(validator, opt.get_validator())
コード例 #2
0
ファイル: discover.py プロジェクト: vduseev/fate
class DiscoverCommand(Command):
    name = "discover"
    description = "Test dsicovery functionality"

    arguments = [
        argument("parameter", description="What to discover", optional=False)
    ]
    options = [
        option("input-glob", flag=False),
        option("output-glob", flag=False)
    ]

    def handle(self) -> None:
        parameter = self.argument("parameter")

        if parameter == "tests":
            input_glob = self.option("input-glob")
            output_glob = self.option("output-glob")

            if not input_glob:
                self.line("<error>input-glob option must be specified</error>")
                return

            if not output_glob:
                self.line(
                    "<error>output-glob option must be specified</error>")
                return

            tests = Discovery.find_test_cases(input_glob, output_glob)
            self.line(f"len of tests: {len(tests)}")

            for t in tests:
                print(f"key: {t}; input: {tests[t][0]}; output: {tests[t][1]}")
コード例 #3
0
ファイル: publish.py プロジェクト: mgasner/poetry
class PublishCommand(Command):

    name = "publish"
    description = "Publishes a package to a remote repository."

    options = [
        option("repository",
               "r",
               "The repository to publish the package to.",
               flag=False),
        option("username",
               "u",
               "The username to access the repository.",
               flag=False),
        option("password",
               "p",
               "The password to access the repository.",
               flag=False),
        option("build", None, "Build the package before publishing."),
    ]

    help = """The publish command builds and uploads the package to a remote repository.

By default, it will upload to PyPI but if you pass the --repository option it will
upload to it instead.

The --repository option should match the name of a configured repository using
the config command.
"""

    def handle(self):
        from poetry.masonry.publishing.publisher import Publisher

        publisher = Publisher(self.poetry, self.io)

        # Building package first, if told
        if self.option("build"):
            if publisher.files:
                if not self.confirm(
                        "There are <info>{}</info> files ready for publishing. "
                        "Build anyway?".format(len(publisher.files))):
                    self.line_error("<error>Aborted!</error>")

                    return 1

            self.call("build")

        files = publisher.files
        if not files:
            self.line_error(
                "<error>No files to publish. "
                "Run poetry build first or use the --build option.</error>")

            return 1

        self.line("")

        publisher.publish(self.option("repository"), self.option("username"),
                          self.option("password"))
コード例 #4
0
ファイル: train_spm.py プロジェクト: Nemexur/nonauto-lm
class TrainSentencePieceModelCommand(BaseCommand):
    name = "train-spm"
    description = "Train Sentence Piece Model for BPE encoding."
    arguments = [argument("config", description="Config to use for SPM training.")]
    options = [
        option(
            "serialization-dir",
            "s",
            description="Directory to save trained SPM Model.",
            flag=False,
            value_required=False,
        ),
        option(
            "extra-vars",
            None,
            description=(
                "Extra variables to inject in JsonNet config in such format: "
                "{key_name1}={new_value1},{key_name2}={new_value2},..."
            ),
            flag=False,
            value_required=False,
        ),
    ]

    def handle(self) -> None:
        extra_vars = self.parse_extra_vars()
        config = Params.from_file(self.argument("config"), ext_vars=extra_vars)
        # Add serialization directory to config and create it
        serialization_dir = Path(self.option("serialization-dir"))
        self.prepare_directory(serialization_dir)
        # Log config to console and save
        config["model_prefix"] = str(serialization_dir / config["model_prefix"])
        logger.info(
            "Config: {}".format(json.dumps(config.as_flat_dict(), indent=2, ensure_ascii=False))
        )
        with (serialization_dir / "config.json").open("w", encoding="utf-8") as file:
            json.dump(config.as_dict(quiet=True), file, indent=2, ensure_ascii=False)
        # Train SPM Model
        spm.SentencePieceTrainer.train(**config)
        self.line(
            f"Finished SentencePiece model training and saved at path: `{serialization_dir}`",
            style="info",
        )

    def parse_extra_vars(self) -> Dict[str, str]:
        extra_vars = self.option("extra-vars")
        regex = r"([a-z0-9\_\-\.\+\\\/]+)=([a-z0-9\_\-\.\+\\\/]+)"
        return (
            {param: value for param, value in re.findall(regex, extra_vars, flags=re.I)}
            if extra_vars is not None else None
        )
コード例 #5
0
class BuildCommand(EnvCommand):

    name = "build"
    description = "Builds a package, as a tarball and a wheel by default."

    options = [
        option("format",
               "f",
               "Limit the format to either wheel or sdist.",
               flag=False)
    ]

    def handle(self):
        from poetry.masonry import Builder

        fmt = "all"
        if self.option("format"):
            fmt = self.option("format")

        package = self.poetry.package
        self.line("Building <info>{}</> (<comment>{}</>)".format(
            package.pretty_name, package.version))

        builder = Builder(self.poetry, self.env, self.io)
        builder.build(fmt)
コード例 #6
0
ファイル: list.py プロジェクト: yudai-nkt/poetry
class EnvListCommand(Command):

    name = "list"
    description = "Lists all virtualenvs associated with the current project."

    options = [
        option("full-path", None, "Output the full paths of the virtualenvs.")
    ]

    def handle(self):
        from poetry.utils.env import EnvManager

        poetry = self.poetry
        manager = EnvManager(poetry.config)
        current_env = manager.get(self.poetry.file.parent)

        for venv in manager.list(self.poetry.file.parent):
            name = venv.path.name
            if self.option("full-path"):
                name = str(venv.path)

            if venv == current_env:
                self.line("<info>{} (Activated)</info>".format(name))

                continue

            self.line(name)
コード例 #7
0
ファイル: lock.py プロジェクト: RivtCalc/replit01
class LockCommand(InstallerCommand):

    name = "lock"
    description = "Locks the project dependencies."

    options = [
        option("no-update", None,
               "Do not update locked versions, only refresh lock file."),
    ]

    help = """
The <info>lock</info> command reads the <comment>pyproject.toml</> file from the
current directory, processes it, and locks the dependencies in the <comment>poetry.lock</>
file.

<info>poetry lock</info>
"""

    loggers = ["poetry.repositories.pypi_repository"]

    def handle(self):
        self._installer.use_executor(
            self.poetry.config.get("experimental.new-installer", False))

        self._installer.lock(update=not self.option("no-update"))

        return self._installer.run()
コード例 #8
0
ファイル: build.py プロジェクト: trevenen/poetry
class BuildCommand(EnvCommand):

    name = "build"
    description = "Builds a package, as a tarball and a wheel by default."

    options = [
        option("format",
               "f",
               "Limit the format to either sdist or wheel.",
               flag=False)
    ]

    loggers = [
        "poetry.core.masonry.builders.sdist",
        "poetry.core.masonry.builders.wheel",
    ]

    def handle(self):
        from poetry.core.masonry import Builder

        fmt = "all"
        if self.option("format"):
            fmt = self.option("format")

        package = self.poetry.package
        self.line("Building <c1>{}</c1> (<c2>{}</c2>)".format(
            package.pretty_name, package.version))

        builder = Builder(self.poetry)
        builder.build(fmt)
コード例 #9
0
class SearchCommand(Command):

    name = "search"
    description = "Searches for packages on remote repositories."

    arguments = [
        argument("tokens", "The tokens to search for.", multiple=True)
    ]
    options = [option("only-name", "N", "Search only by name.")]

    def handle(self):
        from poetry.repositories.pypi_repository import PyPiRepository

        flags = PyPiRepository.SEARCH_FULLTEXT
        if self.option("only-name"):
            flags = PyPiRepository.SEARCH_NAME

        results = PyPiRepository().search(self.argument("tokens"), flags)

        for result in results:
            self.line("")
            name = "<info>{}</>".format(result.name)

            name += " (<comment>{}</>)".format(result.version)

            self.line(name)

            if result.description:
                self.line(" {}".format(result.description))
コード例 #10
0
class ExportCommand(Command):

    name = "export"
    description = "Exports the lock file to alternative formats."

    options = [
        option("format", "f", "Format to export to.", flag=False),
        option("without-hashes", None,
               "Exclude hashes from the exported file."),
        option("dev", None, "Include development dependencies."),
    ]

    def handle(self):
        fmt = self.option("format")

        if fmt not in Exporter.ACCEPTED_FORMATS:
            raise ValueError("Invalid export format: {}".format(fmt))

        locker = self.poetry.locker
        if not locker.is_locked():
            self.line(
                "<comment>The lock file does not exist. Locking.</comment>")
            options = []
            if self.io.is_debug():
                options.append(("-vvv", None))
            elif self.io.is_very_verbose():
                options.append(("-vv", None))
            elif self.io.is_verbose():
                options.append(("-v", None))

            self.call("lock", options)

        if not locker.is_fresh():
            self.line("<warning>"
                      "Warning: The lock file is not up to date with "
                      "the latest changes in pyproject.toml. "
                      "You may be getting outdated dependencies. "
                      "Run update to update them."
                      "</warning>")

        exporter = Exporter(self.poetry.locker)
        exporter.export(
            fmt,
            self.poetry.file.parent,
            with_hashes=not self.option("without-hashes"),
            dev=self.option("dev"),
        )
コード例 #11
0
ファイル: update.py プロジェクト: mgasner/poetry
class UpdateCommand(EnvCommand):

    name = "update"
    description = (
        "Update dependencies as according to the <comment>pyproject.toml</> file."
    )

    arguments = [
        argument("packages",
                 "The packages to update",
                 optional=True,
                 multiple=True)
    ]
    options = [
        option("no-dev", None, "Do not update dev dependencies."),
        option(
            "dry-run",
            None,
            "Output the operations but do not execute anything "
            "(implicitly enables --verbose).",
        ),
        option("lock", None,
               "Do not perform operations (only update the lockfile)."),
    ]

    loggers = ["poetry.repositories.pypi_repository"]

    def handle(self):
        from poetry.installation import Installer

        packages = self.argument("packages")

        installer = Installer(self.io, self.env, self.poetry.package,
                              self.poetry.locker, self.poetry.pool)

        if packages:
            installer.whitelist({name: "*" for name in packages})

        installer.dev_mode(not self.option("no-dev"))
        installer.dry_run(self.option("dry-run"))
        installer.execute_operations(not self.option("lock"))

        # Force update
        installer.update(True)

        return installer.run()
コード例 #12
0
ファイル: run.py プロジェクト: vduseev/fate
class RunCommand(Command):
    name = "run"
    description = "Run fate"

    arguments = [
        argument("path",
                 "The directory in which to run fate or path to executable",
                 optional=True)
    ]
    options = [
        option("output-type",
               description=
               "Instruct executable to print output to file or to stdout",
               value_required=True),
        option("execution-environment",
               description="Which environment to use to run the solution",
               value_required=True),
        option("docker",
               description="Use docker to run the solution",
               flag=True),
        option("debug", description="Run solution in debug mode", flag=True),
        option("input-file",
               description="Path input file of the test case",
               value_required=True),
        option("output-file",
               description="Path to output file of the test case",
               value_required=True),
        option("test-case-name",
               description="Name of the test case to execute",
               value_required=True),
        option(
            "concurrent",
            description=
            "Run tests concurrently (optionally specify number of concurent executions)",
            value_required=False),
        option("memory-limit",
               description=
               "Impose a memory limit (MB) when running in docker container",
               value_required=True),
        option("time-limit",
               description="Impose a time limit (ms) for each execution",
               value_required=True)
    ]

    def handle(self) -> None:
        pass
コード例 #13
0
class ConfigureCommand(Command):
    name = "configure"
    description = "Configure the environment"

    arguments = [
        argument("command",
                 description="Which command to execute: show, get, or set",
                 optional=False),
        argument("parameter",
                 description="Which parameter to set or get",
                 optional=True)
    ]
    options = [
        option(
            "user",
            description=
            "Save configuration file in user's home directory when setting parameter",
            flag=True),
        option(
            "system",
            description=
            "Save configuration file in system directory when setting parameters",
            flag=True)
    ]

    def handle(self) -> None:
        command = self.argument("command")
        parameter = self.argument("parameter")

        if command == "show":
            config = Configuration()
            config.build()
            table = config.as_table()
            self.render_table(*table)

        elif command == "get":
            if not parameter:
                self.line("<error>Parameter name must be specified</error>")

        elif command == "set":
            if not parameter:
                self.line("<error>Parameter name must be specified</error>")

        else:
            self.line(f"<error>Wrong command: {command}</error>")
コード例 #14
0
class UpdateCommand(InstallerCommand):

    name = "update"
    description = (
        "Update the dependencies as according to the <comment>pyproject.toml</> file."
    )

    arguments = [
        argument("packages",
                 "The packages to update",
                 optional=True,
                 multiple=True)
    ]
    options = [
        option("no-dev", None, "Do not update the development dependencies."),
        option(
            "dry-run",
            None,
            "Output the operations but do not execute anything "
            "(implicitly enables --verbose).",
        ),
        option("lock", None,
               "Do not perform operations (only update the lockfile)."),
    ]

    loggers = ["poetry.repositories.pypi_repository"]

    def handle(self):  # type: () -> int
        packages = self.argument("packages")

        self._installer.use_executor(
            self.poetry.config.get("experimental.new-installer", False))

        if packages:
            self._installer.whitelist({name: "*" for name in packages})

        self._installer.dev_mode(not self.option("no-dev"))
        self._installer.dry_run(self.option("dry-run"))
        self._installer.execute_operations(not self.option("lock"))

        # Force update
        self._installer.update(True)

        return self._installer.run()
コード例 #15
0
class EnvInfoCommand(Command):

    name = "info"
    description = "Displays information about the current environment."

    options = [option("path", "p", "Only display the environment's path.")]

    def handle(self):
        from poetry.utils.env import EnvManager

        poetry = self.poetry
        env = EnvManager(poetry.config).get(cwd=poetry.file.parent)

        if self.option("path"):
            if not env.is_venv():
                return 1

            self.write(str(env.path))

            return

        self._display_complete_info(env)

    def _display_complete_info(self, env):
        env_python_version = ".".join(str(s) for s in env.version_info[:3])
        self.line("")
        self.line("<b>Virtualenv</b>")
        listing = [
            "<info>Python</info>:         <comment>{}</>".format(env_python_version),
            "<info>Implementation</info>: <comment>{}</>".format(
                env.python_implementation
            ),
            "<info>Path</info>:           <comment>{}</>".format(
                env.path if env.is_venv() else "NA"
            ),
        ]
        if env.is_venv():
            listing.append(
                "<info>Valid</info>:          <{tag}>{is_valid}</{tag}>".format(
                    tag="comment" if env.is_sane() else "error", is_valid=env.is_sane()
                )
            )
        self.line("\n".join(listing))

        self.line("")

        self.line("<b>System</b>")
        self.line(
            "\n".join(
                [
                    "<info>Platform</info>: <comment>{}</>".format(env.platform),
                    "<info>OS</info>:       <comment>{}</>".format(env.os),
                    "<info>Python</info>:   <comment>{}</>".format(env.base),
                ]
            )
        )
コード例 #16
0
def test_option():
    opt = option("foo", "f", "Foo")

    assert "Foo" == opt.description
    assert not opt.accepts_value()
    assert not opt.is_value_optional()
    assert not opt.is_value_required()
    assert not opt.is_multi_valued()
    assert opt.default is None

    opt = option("foo", "f", "Foo", flag=False)

    assert "Foo" == opt.description
    assert opt.accepts_value()
    assert not opt.is_value_optional()
    assert opt.is_value_required()
    assert not opt.is_multi_valued()

    opt = option("foo", "f", "Foo", flag=False, value_required=False)

    assert "Foo" == opt.description
    assert opt.accepts_value()
    assert opt.is_value_optional()
    assert not opt.is_value_required()
    assert not opt.is_multi_valued()

    opt = option("foo", "f", "Foo", flag=False, multiple=True)

    assert "Foo" == opt.description
    assert opt.accepts_value()
    assert not opt.is_value_optional()
    assert opt.is_value_required()
    assert opt.is_multi_valued()
    assert [] == opt.default

    opt = option("foo", "f", "Foo", flag=False, default="bar")

    assert "Foo" == opt.description
    assert opt.accepts_value()
    assert not opt.is_value_optional()
    assert opt.is_value_required()
    assert not opt.is_multi_valued()
    assert "bar" == opt.default
コード例 #17
0
class OcelCommand(Command):
    name = 'ocel'
    description = 'ocel command'
    arguments = [
        argument('template', 'name of template', optional=True),
        argument('target', 'target path', optional=True)
    ]
    options = [
        option('list', 'l', 'list of templates'),
        option(
            'update',
            'u',
            'update ocel from github',
        )
    ]

    def handle(self):
        ls = self.option('list')
        update = self.option('update')
        template = self.argument('template')
        target = self.argument('target')
        ocel = Ocel()

        if template and (ls or update):
            raise CommandError('Invalid format of command')

        if template:
            ocel.run(template, target)
            self.line('Finished.')
        elif ls:
            template_list = ocel.template_list(True)
            table = self.table(['Name', 'Desc'])
            for name, meta in template_list.items():
                table.add_row([name, meta.get('desc', '')])
            table.render(self.io)
        elif update:
            ocel.update()
        else:
            raise CommandError('Invalid format of command')
コード例 #18
0
ファイル: shards.py プロジェクト: Nemexur/nonauto-lm
class MakeShardsCommand(BaseCommand):
    name = "make-shards"
    description = "Construct shards for Distributed Training from one txt file."
    arguments = [argument("path", description="Path to txt file.")]
    options = [
        option(
            "shards",
            "s",
            description="Number of shrads to split txt file.",
            flag=False,
            value_required=False,
        ),
        option(
            "seed",
            None,
            default=13,
            description="Random seed for to randomly assign sample to a shard.",
            flag=False,
            value_required=False,
        ),
    ]

    def handle(self) -> None:
        rng = random.Random(int(self.option("seed")))
        shards = int(self.option("shards"))
        file_path = Path(self.argument("path"))
        directory = file_path.parent / file_path.stem
        self.prepare_directory(directory)
        with file_path.open("r", encoding="utf-8") as file:
            for line in tqdm(map(lambda x: x.strip(), file),
                             desc="Sharding dataset"):
                choice = rng.randint(0, shards - 1)
                self.write_to_shard(line, directory / f"shard-{choice}.txt")

    @staticmethod
    def write_to_shard(line: str, shard_path: Path) -> None:
        with shard_path.open("a", encoding="utf-8") as file:
            file.write(line + "\n")
コード例 #19
0
class CloneCommand(Command):
    name = 'clone'
    description = 'clone repository from github'
    arguments = [
        argument('repository', 'name of repository that you want to clone'),
        argument('target', 'target directory name', optional=True),
    ]
    options = [option('url', 'u', 'echo only url, not clone')]

    def handle(self):
        clone_url = url_parsing(self.argument('repository'))
        if self.option('url'):
            self.line(clone_url)
            return 1
        clone(clone_url, self.argument('target'))
コード例 #20
0
ファイル: startapp.py プロジェクト: dhararon/flubber
class StartAppCommand(Command):

    name = "startapp"
    description = "Create a new flubber app"

    arguments = [argument("name", "The app name.")]

    options = [option("path", None, "The path to create the app at.", flag=False)]

    def handle(self) -> None:
        from flubber.console.commands.utils import get_flubber_path

        if self.option("path"):
            path = Path(self.option("path")) / Path(self.argument("name"))
        else:
            path = Path.cwd() / Path(self.argument("name"))

        name = self.argument("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)
                )

        self.copy_project_folder(src=get_flubber_path(), dst=path)

        self.line(
            "Created flubber project <info>{}</> in <fg=blue>{}</>".format(
                name, path.relative_to(Path.cwd())
            )
        )

    def copy_project_folder(self, src: str, dst: str) -> None:
        import shutil

        self.line("<info>Creating project ...</>")

        template_project_path = Path(src) / Path("conf/app_template")

        dst_path = Path(dst)
        shutil.copytree(template_project_path, dst_path)
        return
コード例 #21
0
class ExportCommand(Command):

    name = "export"
    description = "Exports the lock file to alternative formats."

    options = [
        option(
            "format",
            "f",
            "Format to export to. Currently, only requirements.txt is supported.",
            flag=False,
            default=Exporter.FORMAT_REQUIREMENTS_TXT,
        ),
        option("output", "o", "The name of the output file.", flag=False),
        option("without-hashes", None,
               "Exclude hashes from the exported file."),
        option("dev", None, "Include development dependencies."),
        option(
            "extras",
            "E",
            "Extra sets of dependencies to include.",
            flag=False,
            multiple=True,
        ),
        option("with-credentials", None,
               "Include credentials for extra indices."),
    ]

    def handle(self):  # type: () -> None
        fmt = self.option("format")

        if fmt not in Exporter.ACCEPTED_FORMATS:
            raise ValueError("Invalid export format: {}".format(fmt))

        output = self.option("output")

        locker = self.poetry.locker
        if not locker.is_locked():
            self.line(
                "<comment>The lock file does not exist. Locking.</comment>")
            options = []
            if self.io.is_debug():
                options.append(("-vvv", None))
            elif self.io.is_very_verbose():
                options.append(("-vv", None))
            elif self.io.is_verbose():
                options.append(("-v", None))

            self.call("lock", options)

        if not locker.is_fresh():
            self.line("<warning>"
                      "Warning: The lock file is not up to date with "
                      "the latest changes in pyproject.toml. "
                      "You may be getting outdated dependencies. "
                      "Run update to update them."
                      "</warning>")

        exporter = Exporter(self.poetry)
        exporter.export(
            fmt,
            self.poetry.file.parent,
            output or self.io,
            with_hashes=not self.option("without-hashes"),
            dev=self.option("dev"),
            extras=self.option("extras"),
            with_credentials=self.option("with-credentials"),
        )
コード例 #22
0
ファイル: resolve.py プロジェクト: trevenen/poetry
class DebugResolveCommand(InitCommand):

    name = "resolve"
    description = "Debugs dependency resolution."

    arguments = [
        argument("package", "The packages to resolve.", optional=True, multiple=True)
    ]
    options = [
        option(
            "extras",
            "E",
            "Extras to activate for the dependency.",
            flag=False,
            multiple=True,
        ),
        option("python", None, "Python version(s) to use for resolution.", flag=False),
        option("tree", None, "Display the dependency tree."),
        option("install", None, "Show what would be installed for the current system."),
    ]

    loggers = ["poetry.repositories.pypi_repository"]

    def handle(self):
        from poetry.core.packages.project_package import ProjectPackage
        from poetry.io.null_io import NullIO
        from poetry.puzzle import Solver
        from poetry.repositories.pool import Pool
        from poetry.repositories.repository import Repository
        from poetry.utils.env import EnvManager

        packages = self.argument("package")

        if not packages:
            package = self.poetry.package
        else:
            # Using current pool for determine_requirements()
            self._pool = self.poetry.pool

            package = ProjectPackage(
                self.poetry.package.name, self.poetry.package.version
            )

            # Silencing output
            is_quiet = self.io.output.is_quiet()
            if not is_quiet:
                self.io.output.set_quiet(True)

            requirements = self._determine_requirements(packages)

            if not is_quiet:
                self.io.output.set_quiet(False)

            for constraint in requirements:
                name = constraint.pop("name")
                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._io)

        ops = solver.solve()

        self.line("")
        self.line("Resolution results:")
        self.line("")

        if self.option("tree"):
            show_command = self.application.find("show")
            show_command.init_styles(self.io)

            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(self.io, pkg, repo)
                        break

            return 0

        table = self.table([], style="borderless")
        rows = []

        if self.option("install"):
            env = EnvManager(self.poetry).get()
            pool = Pool()
            locked_repository = Repository()
            for op in ops:
                locked_repository.add_package(op.package)

            pool.add_repository(locked_repository)

            solver = Solver(package, pool, Repository(), Repository(), NullIO())
            with solver.use_environment(env):
                ops = solver.solve()

        for op in ops:
            if self.option("install") and op.skipped:
                continue

            pkg = op.package
            row = [
                "<c1>{}</c1>".format(pkg.name),
                "<b>{}</b>".format(pkg.version),
                "",
            ]

            if not pkg.marker.is_any():
                row[2] = str(pkg.marker)

            rows.append(row)

        table.set_rows(rows)
        table.render(self.io)
コード例 #23
0
class ConfigCommand(Command):

    name = "config"
    description = "Manages configuration settings."

    arguments = [
        argument("key", "Setting key.", optional=True),
        argument("value", "Setting value.", optional=True, multiple=True),
    ]

    options = [
        option("list", None, "List configuration settings."),
        option("unset", None, "Unset configuration setting."),
        option("local", None,
               "Set/Get from the project's local configuration."),
    ]

    help = """This command allows you to edit the poetry config settings and repositories.

To add a repository:

    <comment>poetry config repositories.foo https://bar.com/simple/</comment>

To remove a repository (repo is a short alias for repositories):

    <comment>poetry config --unset repo.foo</comment>"""

    LIST_PROHIBITED_SETTINGS = {"http-basic", "pypi-token"}

    @property
    def unique_config_values(self):
        from poetry.config.config import boolean_normalizer
        from poetry.config.config import boolean_validator
        from poetry.locations import CACHE_DIR
        from poetry.utils._compat import Path

        unique_config_values = {
            "cache-dir": (
                str,
                lambda val: str(Path(val)),
                str(Path(CACHE_DIR) / "virtualenvs"),
            ),
            "virtualenvs.create":
            (boolean_validator, boolean_normalizer, True),
            "virtualenvs.in-project":
            (boolean_validator, boolean_normalizer, False),
            "virtualenvs.path": (
                str,
                lambda val: str(Path(val)),
                str(Path(CACHE_DIR) / "virtualenvs"),
            ),
        }

        return unique_config_values

    def handle(self):
        from poetry.config.file_config_source import FileConfigSource
        from poetry.locations import CONFIG_DIR
        from poetry.utils._compat import Path
        from poetry.utils._compat import basestring
        from poetry.utils.toml_file import TomlFile

        config = Factory.create_config(self.io)
        config_file = TomlFile(Path(CONFIG_DIR) / "config.toml")

        try:
            local_config_file = TomlFile(self.poetry.file.parent /
                                         "poetry.toml")
            if local_config_file.exists():
                config.merge(local_config_file.read())
        except RuntimeError:
            local_config_file = TomlFile(Path.cwd() / "poetry.toml")

        if self.option("local"):
            config.set_config_source(FileConfigSource(local_config_file))

        if not config_file.exists():
            config_file.path.parent.mkdir(parents=True, exist_ok=True)
            config_file.touch(mode=0o0600)

        if self.option("list"):
            self._list_configuration(config.all(), config.raw())

            return 0

        setting_key = self.argument("key")
        if not setting_key:
            return 0

        if self.argument("value") and self.option("unset"):
            raise RuntimeError(
                "You can not combine a setting value with --unset")

        # show the value if no value is provided
        if not self.argument("value") and not self.option("unset"):
            m = re.match(r"^repos?(?:itories)?(?:\.(.+))?",
                         self.argument("key"))
            if m:
                if not m.group(1):
                    value = {}
                    if config.get("repositories") is not None:
                        value = config.get("repositories")
                else:
                    repo = config.get("repositories.{}".format(m.group(1)))
                    if repo is None:
                        raise ValueError(
                            "There is no {} repository defined".format(
                                m.group(1)))

                    value = repo

                self.line(str(value))
            else:
                values = self.unique_config_values
                if setting_key not in values:
                    raise ValueError(
                        "There is no {} setting.".format(setting_key))

                value = config.get(setting_key)

                if not isinstance(value, basestring):
                    value = json.dumps(value)

                self.line(value)

            return 0

        values = self.argument("value")

        unique_config_values = self.unique_config_values
        if setting_key in unique_config_values:
            if self.option("unset"):
                return config.config_source.remove_property(setting_key)

            return self._handle_single_value(
                config.config_source,
                setting_key,
                unique_config_values[setting_key],
                values,
            )

        # handle repositories
        m = re.match(r"^repos?(?:itories)?(?:\.(.+))?", self.argument("key"))
        if m:
            if not m.group(1):
                raise ValueError(
                    "You cannot remove the [repositories] section")

            if self.option("unset"):
                repo = config.get("repositories.{}".format(m.group(1)))
                if repo is None:
                    raise ValueError(
                        "There is no {} repository defined".format(m.group(1)))

                config.config_source.remove_property("repositories.{}".format(
                    m.group(1)))

                return 0

            if len(values) == 1:
                url = values[0]

                config.config_source.add_property(
                    "repositories.{}.url".format(m.group(1)), url)

                return 0

            raise ValueError(
                "You must pass the url. "
                "Example: poetry config repositories.foo https://bar.com")

        # handle auth
        m = re.match(r"^(http-basic|pypi-token)\.(.+)", self.argument("key"))
        if m:
            if self.option("unset"):
                keyring_repository_password_del(config, m.group(2))
                config.auth_config_source.remove_property("{}.{}".format(
                    m.group(1), m.group(2)))

                return 0

            if m.group(1) == "http-basic":
                if len(values) == 1:
                    username = values[0]
                    # Only username, so we prompt for password
                    password = self.secret("Password:"******"Expected one or two arguments "
                                     "(username, password), got {}".format(
                                         len(values)))
                else:
                    username = values[0]
                    password = values[1]

                property_value = dict(username=username)
                try:
                    keyring_repository_password_set(m.group(2), username,
                                                    password)
                except RuntimeError:
                    property_value.update(password=password)

                config.auth_config_source.add_property(
                    "{}.{}".format(m.group(1), m.group(2)), property_value)
            elif m.group(1) == "pypi-token":
                if len(values) != 1:
                    raise ValueError(
                        "Expected only one argument (token), got {}".format(
                            len(values)))

                token = values[0]

                config.auth_config_source.add_property(
                    "{}.{}".format(m.group(1), m.group(2)), token)

            return 0

        # handle certs
        m = re.match(r"(?:certificates)\.([^.]+)\.(cert|client-cert)",
                     self.argument("key"))
        if m:
            if self.option("unset"):
                config.auth_config_source.remove_property(
                    "certificates.{}.{}".format(m.group(1), m.group(2)))

                return 0

            if len(values) == 1:
                config.auth_config_source.add_property(
                    "certificates.{}.{}".format(m.group(1), m.group(2)),
                    values[0])
            else:
                raise ValueError("You must pass exactly 1 value")

            return 0

        raise ValueError("Setting {} does not exist".format(
            self.argument("key")))

    def _handle_single_value(self, source, key, callbacks, values):
        validator, normalizer, _ = callbacks

        if len(values) > 1:
            raise RuntimeError("You can only pass one value.")

        value = values[0]
        if not validator(value):
            raise RuntimeError('"{}" is an invalid value for {}'.format(
                value, key))

        source.add_property(key, normalizer(value))

        return 0

    def _list_configuration(self, config, raw, k=""):
        from poetry.utils._compat import basestring

        orig_k = k
        for key, value in sorted(config.items()):
            if k + key in self.LIST_PROHIBITED_SETTINGS:
                continue

            raw_val = raw.get(key)

            if isinstance(value, dict):
                k += "{}.".format(key)
                self._list_configuration(value, raw_val, k=k)
                k = orig_k

                continue
            elif isinstance(value, list):
                value = [
                    json.dumps(val) if isinstance(val, list) else val
                    for val in value
                ]

                value = "[{}]".format(", ".join(value))

            if k.startswith("repositories."):
                message = "<c1>{}</c1> = <c2>{}</c2>".format(
                    k + key, json.dumps(raw_val))
            elif isinstance(raw_val, basestring) and raw_val != value:
                message = "<c1>{}</c1> = <c2>{}</c2>  # {}".format(
                    k + key, json.dumps(raw_val), value)
            else:
                message = "<c1>{}</c1> = <c2>{}</c2>".format(
                    k + key, json.dumps(value))

            self.line(message)

    def _list_setting(self, contents, setting=None, k=None, default=None):
        values = self._get_setting(contents, setting, k, default)

        for value in values:
            self.line("<comment>{}</comment> = <info>{}</info>".format(
                value[0], value[1]))

    def _get_setting(self, contents, setting=None, k=None, default=None):
        orig_k = k

        if setting and setting.split(".")[0] not in contents:
            value = json.dumps(default)

            return [((k or "") + setting, value)]
        else:
            values = []
            for key, value in contents.items():
                if setting and key != setting.split(".")[0]:
                    continue

                if isinstance(value,
                              dict) or key == "repositories" and k is None:
                    if k is None:
                        k = ""

                    k += re.sub(r"^config\.", "", key + ".")
                    if setting and len(setting) > 1:
                        setting = ".".join(setting.split(".")[1:])

                    values += self._get_setting(value,
                                                k=k,
                                                setting=setting,
                                                default=default)
                    k = orig_k

                    continue

                if isinstance(value, list):
                    value = [
                        json.dumps(val) if isinstance(val, list) else val
                        for val in value
                    ]

                    value = "[{}]".format(", ".join(value))

                value = json.dumps(value)

                values.append(((k or "") + key, value))

            return values

    def _get_formatted_value(self, value):
        if isinstance(value, list):
            value = [
                json.dumps(val) if isinstance(val, list) else val
                for val in value
            ]

            value = "[{}]".format(", ".join(value))

        return json.dumps(value)
コード例 #24
0
ファイル: version.py プロジェクト: yosmoc/poetry
class VersionCommand(Command):

    name = "version"
    description = ("Shows the version of the project or bumps it when a valid "
                   "bump rule is provided.")

    arguments = [
        argument(
            "version",
            "The version number or the rule to update the version.",
            optional=True,
        )
    ]
    options = [option("short", "s", "Output the version number only")]

    help = """\
The version command shows the current version of the project or bumps the version of
the project and writes the new version back to <comment>pyproject.toml</> if a valid
bump rule is provided.

The new version should ideally be a valid semver string or a valid bump rule:
patch, minor, major, prepatch, preminor, premajor, prerelease.
"""

    RESERVED = {
        "major",
        "minor",
        "patch",
        "premajor",
        "preminor",
        "prepatch",
        "prerelease",
    }

    def handle(self):
        version = self.argument("version")

        if version:
            version = self.increment_version(
                self.poetry.package.pretty_version, version)

            if self.option("short"):
                self.line("{}".format(version))
            else:
                self.line(
                    "Bumping version from <b>{}</> to <fg=green>{}</>".format(
                        self.poetry.package.pretty_version, version))

            content = self.poetry.file.read()
            poetry_content = content["tool"]["poetry"]
            poetry_content["version"] = version.text

            self.poetry.file.write(content)
        else:
            if self.option("short"):
                self.line("{}".format(self.poetry.package.pretty_version))
            else:
                self.line("<comment>{}</> <info>{}</>".format(
                    self.poetry.package.name,
                    self.poetry.package.pretty_version))

    def increment_version(self, version, rule):
        from poetry.core.semver import Version

        try:
            version = Version.parse(version)
        except ValueError:
            raise ValueError(
                "The project's version doesn't seem to follow semver")

        if rule in {"major", "premajor"}:
            new = version.next_major
            if rule == "premajor":
                new = new.first_prerelease
        elif rule in {"minor", "preminor"}:
            new = version.next_minor
            if rule == "preminor":
                new = new.first_prerelease
        elif rule in {"patch", "prepatch"}:
            new = version.next_patch
            if rule == "prepatch":
                new = new.first_prerelease
        elif rule == "prerelease":
            if version.is_prerelease():
                pre = version.prerelease
                new_prerelease = int(pre[1]) + 1
                new = Version.parse("{}.{}.{}-{}".format(
                    version.major,
                    version.minor,
                    version.patch,
                    ".".join([pre[0], str(new_prerelease)]),
                ))
            else:
                new = version.next_patch.first_prerelease
        else:
            new = Version.parse(rule)

        return new
コード例 #25
0
class InstallCommand(EnvCommand):

    name = "install"
    description = "Installs the project dependencies."

    options = [
        option("no-dev", None, "Do not install dev dependencies."),
        option(
            "no-root", None, "Do not install the root package (the current project)."
        ),
        option(
            "dry-run",
            None,
            "Outputs the operations but will not execute anything "
            "(implicitly enables --verbose).",
        ),
        option(
            "extras",
            "E",
            "Extra sets of dependencies to install.",
            flag=False,
            multiple=True,
        ),
        option(
            "develop",
            None,
            "Install given packages in development mode.",
            flag=False,
            multiple=True,
        ),
    ]

    help = """The <info>install</info> command reads the <comment>poetry.lock</> file from
the current directory, processes it, and downloads and installs all the
libraries and dependencies outlined in that file. If the file does not
exist it will look for <comment>pyproject.toml</> and do the same.

<info>poetry install</info>
"""

    _loggers = ["poetry.repositories.pypi_repository"]

    def handle(self):
        from clikit.io import NullIO
        from poetry.installation import Installer
        from poetry.masonry.builders import EditableBuilder
        from poetry.masonry.utils.module import ModuleOrPackageNotFound

        installer = Installer(
            self.io, self.env, self.poetry.package, self.poetry.locker, self.poetry.pool
        )

        extras = []
        for extra in self.option("extras"):
            if " " in extra:
                extras += [e.strip() for e in extra.split(" ")]
            else:
                extras.append(extra)

        installer.extras(extras)
        installer.dev_mode(not self.option("no-dev"))
        installer.develop(self.option("develop"))
        installer.dry_run(self.option("dry-run"))
        installer.verbose(self.option("verbose"))

        return_code = installer.run()

        if return_code != 0:
            return return_code

        if self.option("no-root"):
            return 0

        try:
            builder = EditableBuilder(self.poetry, self._env, NullIO())
        except ModuleOrPackageNotFound:
            # This is likely due to the fact that the project is an application
            # not following the structure expected by Poetry
            # If this is a true error it will be picked up later by build anyway.
            return 0

        self.line(
            "  - Installing <info>{}</info> (<comment>{}</comment>)".format(
                self.poetry.package.pretty_name, self.poetry.package.pretty_version
            )
        )

        if self.option("dry-run"):
            return 0

        builder.build()

        return 0
コード例 #26
0
class AddCommand(InstallerCommand, InitCommand):

    name = "add"
    description = "Adds a new dependency to <comment>pyproject.toml</>."

    arguments = [argument("name", "The packages to add.", multiple=True)]
    options = [
        option("dev", "D", "Add as a development dependency."),
        option(
            "extras",
            "E",
            "Extras to activate for the dependency.",
            flag=False,
            multiple=True,
        ),
        option("optional", None, "Add as an optional dependency."),
        option(
            "python",
            None,
            "Python version for which the dependency must be installed.",
            flag=False,
        ),
        option(
            "platform",
            None,
            "Platforms for which the dependency must be installed.",
            flag=False,
        ),
        option(
            "source",
            None,
            "Name of the source to use to install the package.",
            flag=False,
        ),
        option("allow-prereleases", None, "Accept prereleases."),
        option(
            "dry-run",
            None,
            "Output the operations but do not execute anything (implicitly enables --verbose).",
        ),
        option("lock", None,
               "Do not perform operations (only update the lockfile)."),
    ]
    help = (
        "The add command adds required packages to your <comment>pyproject.toml</> and installs them.\n\n"
        "If you do not specify a version constraint, poetry will choose a suitable one based on the available package versions.\n\n"
        "You can specify a package in the following forms:\n"
        "  - A single name (<b>requests</b>)\n"
        "  - A name and a constraint (<b>requests@^2.23.0</b>)\n"
        "  - A git url (<b>git+https://github.com/python-poetry/poetry.git</b>)\n"
        "  - A git url with a revision (<b>git+https://github.com/python-poetry/poetry.git#develop</b>)\n"
        "  - A file path (<b>../my-package/my-package.whl</b>)\n"
        "  - A directory (<b>../my-package/</b>)\n"
        "  - A url (<b>https://example.com/packages/my-package-0.1.0.tar.gz</b>)\n"
    )

    loggers = ["poetry.repositories.pypi_repository", "poetry.inspection.info"]

    def handle(self):
        from poetry.core.semver import parse_constraint
        from tomlkit import inline_table

        packages = self.argument("name")
        is_dev = self.option("dev")

        if self.option("extras") and len(packages) > 1:
            raise ValueError("You can only specify one package "
                             "when using the --extras option")

        section = "dependencies"
        if is_dev:
            section = "dev-dependencies"

        original_content = self.poetry.file.read()
        content = self.poetry.file.read()
        poetry_content = content["tool"]["poetry"]

        if section not in poetry_content:
            poetry_content[section] = {}

        for name in packages:
            for key in poetry_content[section]:
                if key.lower() == name.lower():
                    pair = self._parse_requirements([name])[0]
                    if ("git" in pair or "url" in pair
                            or pair.get("version") == "latest"):
                        continue

                    raise ValueError(
                        "Package {} is already present".format(name))

        requirements = self._determine_requirements(
            packages,
            allow_prereleases=self.option("allow-prereleases"),
            source=self.option("source"),
        )

        for _constraint in requirements:
            if "version" in _constraint:
                # Validate version constraint
                parse_constraint(_constraint["version"])

            constraint = inline_table()
            for name, value in _constraint.items():
                if name == "name":
                    continue

                constraint[name] = value

            if self.option("optional"):
                constraint["optional"] = True

            if self.option("allow-prereleases"):
                constraint["allow-prereleases"] = True

            if self.option("extras"):
                extras = []
                for extra in self.option("extras"):
                    if " " in extra:
                        extras += [e.strip() for e in extra.split(" ")]
                    else:
                        extras.append(extra)

                constraint["extras"] = self.option("extras")

            if self.option("python"):
                constraint["python"] = self.option("python")

            if self.option("platform"):
                constraint["platform"] = self.option("platform")

            if self.option("source"):
                constraint["source"] = self.option("source")

            if len(constraint) == 1 and "version" in constraint:
                constraint = constraint["version"]

            poetry_content[section][_constraint["name"]] = constraint

        # Write new content
        self.poetry.file.write(content)

        # Cosmetic new line
        self.line("")

        # Update packages
        self.reset_poetry()

        self._installer.set_package(self.poetry.package)
        self._installer.dry_run(self.option("dry-run"))
        self._installer.verbose(self._io.is_verbose())
        self._installer.update(True)
        if self.option("lock"):
            self._installer.lock()

        self._installer.whitelist([r["name"] for r in requirements])

        try:
            status = self._installer.run()
        except Exception:
            self.poetry.file.write(original_content)

            raise

        if status != 0 or self.option("dry-run"):
            # Revert changes
            if not self.option("dry-run"):
                self.line_error(
                    "\n"
                    "<error>Failed to add packages, reverting the pyproject.toml file "
                    "to its original content.</error>")

            self.poetry.file.write(original_content)

        return status
コード例 #27
0
ファイル: remove.py プロジェクト: mgasner/poetry
class RemoveCommand(EnvCommand):

    name = "remove"
    description = "Removes a package from the project dependencies."

    arguments = [
        argument("packages", "Packages that should be removed", multiple=True)
    ]
    options = [
        option("dev", "D",
               "Removes a package from the development dependencies."),
        option(
            "dry-run",
            None,
            "Outputs the operations but will not execute anything "
            "(implicitly enables --verbose).",
        ),
    ]

    help = """The <info>remove</info> command removes a package from the current
list of installed packages

<info>poetry remove</info>"""

    loggers = ["poetry.repositories.pypi_repository"]

    def handle(self):
        from poetry.installation import Installer

        packages = self.argument("packages")
        is_dev = self.option("dev")

        original_content = self.poetry.file.read()
        content = self.poetry.file.read()
        poetry_content = content["tool"]["poetry"]
        section = "dependencies"
        if is_dev:
            section = "dev-dependencies"

        # Deleting entries
        requirements = {}
        for name in packages:
            found = False
            for key in poetry_content[section]:
                if key.lower() == name.lower():
                    found = True
                    requirements[key] = poetry_content[section][key]
                    break

            if not found:
                raise ValueError("Package {} not found".format(name))

        for key in requirements:
            del poetry_content[section][key]

        # Write the new content back
        self.poetry.file.write(content)

        # Update packages
        self.reset_poetry()

        installer = Installer(self.io, self.env, self.poetry.package,
                              self.poetry.locker, self.poetry.pool)

        installer.dry_run(self.option("dry-run"))
        installer.update(True)
        installer.whitelist(requirements)

        try:
            status = installer.run()
        except Exception:
            self.poetry.file.write(original_content)

            raise

        if status != 0 or self.option("dry-run"):
            # Revert changes
            if not self.option("dry-run"):
                self.error("\n"
                           "Removal failed, reverting pyproject.toml "
                           "to its original content.")

            self.poetry.file.write(original_content)

        return status
コード例 #28
0
class SelfUpdateCommand(Command):

    name = "update"
    description = "Updates Poetry to the latest version."

    arguments = [
        argument("version", "The version to update to.", optional=True)
    ]
    options = [option("preview", None, "Install prereleases.")]

    REPOSITORY_URL = "https://github.com/python-poetry/poetry"
    BASE_URL = REPOSITORY_URL + "/releases/download"

    @property
    def home(self):  # type: () -> Path
        from pathlib import Path

        return Path(os.environ.get("POETRY_HOME", "~/.poetry")).expanduser()

    @property
    def bin(self):  # type: () -> Path
        return self.home / "bin"

    @property
    def lib(self):  # type: () -> Path
        return self.home / "lib"

    @property
    def lib_backup(self):  # type: () -> Path
        return self.home / "lib-backup"

    def handle(self):  # type: () -> None
        from poetry.__version__ import __version__
        from poetry.core.semver import Version
        from poetry.repositories.pypi_repository import PyPiRepository

        self._check_recommended_installation()

        version = self.argument("version")
        if not version:
            version = ">=" + __version__

        repo = PyPiRepository(fallback=False)
        packages = repo.find_packages(
            Dependency("poetry",
                       version,
                       allows_prereleases=self.option("preview")))
        if not packages:
            self.line("No release found for the specified version")
            return

        packages.sort(key=cmp_to_key(lambda x, y: 0 if x.version == y.version
                                     else int(x.version < y.version or -1)))

        release = None
        for package in packages:
            if package.is_prerelease():
                if self.option("preview"):
                    release = package

                    break

                continue

            release = package

            break

        if release is None:
            self.line("No new release found")
            return

        if release.version == Version.parse(__version__):
            self.line("You are using the latest version")
            return

        self.update(release)

    def update(self, release):  # type: ("Package") -> None
        version = release.version
        self.line("Updating to <info>{}</info>".format(version))

        if self.lib_backup.exists():
            shutil.rmtree(str(self.lib_backup))

        # Backup the current installation
        if self.lib.exists():
            shutil.copytree(str(self.lib), str(self.lib_backup))
            shutil.rmtree(str(self.lib))

        try:
            self._update(version)
        except Exception:
            if not self.lib_backup.exists():
                raise

            shutil.copytree(str(self.lib_backup), str(self.lib))
            shutil.rmtree(str(self.lib_backup))

            raise
        finally:
            if self.lib_backup.exists():
                shutil.rmtree(str(self.lib_backup))

        self.make_bin()

        self.line("")
        self.line("")
        self.line(
            "<info>Poetry</info> (<comment>{}</comment>) is installed now. Great!"
            .format(version))

    def _update(self, version):  # type: ("Version") -> None
        from poetry.utils.helpers import temporary_directory

        release_name = self._get_release_name(version)

        checksum = "{}.sha256sum".format(release_name)

        base_url = self.BASE_URL

        try:
            r = urlopen(base_url + "/{}/{}".format(version, checksum))
        except HTTPError as e:
            if e.code == 404:
                raise RuntimeError("Could not find {} file".format(checksum))

            raise

        checksum = r.read().decode().strip()

        # We get the payload from the remote host
        name = "{}.tar.gz".format(release_name)
        try:
            r = urlopen(base_url + "/{}/{}".format(version, name))
        except HTTPError as e:
            if e.code == 404:
                raise RuntimeError("Could not find {} file".format(name))

            raise

        meta = r.info()
        size = int(meta["Content-Length"])
        current = 0
        block_size = 8192

        bar = self.progress_bar(max=size)
        bar.set_format(
            " - Downloading <info>{}</> <comment>%percent%%</>".format(name))
        bar.start()

        sha = hashlib.sha256()
        with temporary_directory(prefix="poetry-updater-") as dir_:
            tar = os.path.join(dir_, name)
            with open(tar, "wb") as f:
                while True:
                    buffer = r.read(block_size)
                    if not buffer:
                        break

                    current += len(buffer)
                    f.write(buffer)
                    sha.update(buffer)

                    bar.set_progress(current)

            bar.finish()

            # Checking hashes
            if checksum != sha.hexdigest():
                raise RuntimeError(
                    "Hashes for {} do not match: {} != {}".format(
                        name, checksum, sha.hexdigest()))

            gz = GzipFile(tar, mode="rb")
            try:
                with tarfile.TarFile(tar,
                                     fileobj=gz,
                                     format=tarfile.PAX_FORMAT) as f:
                    f.extractall(str(self.lib))
            finally:
                gz.close()

    def process(self, *args):  # type: (*Any) -> str
        return subprocess.check_output(list(args), stderr=subprocess.STDOUT)

    def _check_recommended_installation(self):  # type: () -> None
        from pathlib import Path

        current = Path(__file__)
        try:
            current.relative_to(self.home)
        except ValueError:
            raise RuntimeError(
                "Poetry was not installed with the recommended installer. "
                "Cannot update automatically.")

    def _get_release_name(self, version):  # type: ("Version") -> str
        platform = sys.platform
        if platform == "linux2":
            platform = "linux"

        return "poetry-{}-{}".format(version, platform)

    def make_bin(self):  # type: () -> None
        from poetry.utils._compat import WINDOWS

        self.bin.mkdir(0o755, parents=True, exist_ok=True)

        python_executable = self._which_python()

        if WINDOWS:
            with self.bin.joinpath("poetry.bat").open("w", newline="") as f:
                f.write(
                    BAT.format(
                        python_executable=python_executable,
                        poetry_bin=str(self.bin / "poetry").replace(
                            os.environ["USERPROFILE"], "%USERPROFILE%"),
                    ))

        bin_content = BIN
        if not WINDOWS:
            bin_content = "#!/usr/bin/env {}\n".format(
                python_executable) + bin_content

        self.bin.joinpath("poetry").write_text(bin_content, encoding="utf-8")

        if not WINDOWS:
            # Making the file executable
            st = os.stat(str(self.bin.joinpath("poetry")))
            os.chmod(str(self.bin.joinpath("poetry")),
                     st.st_mode | stat.S_IEXEC)

    def _which_python(self):  # type: () -> str
        """
        Decides which python executable we'll embed in the launcher script.
        """
        from poetry.utils._compat import WINDOWS

        allowed_executables = ["python", "python3"]
        if WINDOWS:
            allowed_executables += ["py.exe -3", "py.exe -2"]

        # \d in regex ensures we can convert to int later
        version_matcher = re.compile(
            r"^Python (?P<major>\d+)\.(?P<minor>\d+)\..+$")
        fallback = None
        for executable in allowed_executables:
            try:
                raw_version = subprocess.check_output(
                    executable + " --version",
                    stderr=subprocess.STDOUT,
                    shell=True).decode("utf-8")
            except subprocess.CalledProcessError:
                continue

            match = version_matcher.match(raw_version.strip())
            if match and tuple(map(int, match.groups())) >= (3, 0):
                # favor the first py3 executable we can find.
                return executable

            if fallback is None:
                # keep this one as the fallback; it was the first valid executable we found.
                fallback = executable

        if fallback is None:
            # Avoid breaking existing scripts
            fallback = "python"

        return fallback
コード例 #29
0
ファイル: show.py プロジェクト: yashingle/poetry
class ShowCommand(EnvCommand):

    name = "show"
    description = "Shows information about packages."

    arguments = [argument("package", "The package to inspect", optional=True)]
    options = [
        option("no-dev", None, "Do not list the development dependencies."),
        option("tree", "t", "List the dependencies as a tree."),
        option("latest", "l", "Show the latest version."),
        option(
            "outdated",
            "o",
            "Show the latest version but only for packages that are outdated.",
        ),
        option(
            "all",
            "a",
            "Show all packages (even those not compatible with current system).",
        ),
    ]

    help = """The show command displays detailed information about a package, or
lists all packages available."""

    colors = ["cyan", "yellow", "green", "magenta", "blue"]

    def handle(self):
        from clikit.utils.terminal import Terminal

        from poetry.io.null_io import NullIO
        from poetry.puzzle.solver import Solver
        from poetry.repositories.installed_repository import InstalledRepository
        from poetry.repositories.pool import Pool
        from poetry.repositories.repository import Repository
        from poetry.utils.helpers import get_package_version_display_string

        package = self.argument("package")

        if self.option("tree"):
            self.init_styles(self.io)

        if self.option("outdated"):
            self._args.set_option("latest", True)

        include_dev = not self.option("no-dev")
        locked_repo = self.poetry.locker.locked_repository(True)

        # Show tree view if requested
        if self.option("tree") and not package:
            requires = self.poetry.package.requires + self.poetry.package.dev_requires
            packages = locked_repo.packages
            for package in packages:
                for require in requires:
                    if package.name == require.name:
                        self.display_package_tree(self._io, package,
                                                  locked_repo)
                        break

            return 0

        table = self.table(style="compact")
        # table.style.line_vc_char = ""
        locked_packages = locked_repo.packages
        pool = Pool(ignore_repository_names=True)
        pool.add_repository(locked_repo)
        solver = Solver(
            self.poetry.package,
            pool=pool,
            installed=Repository(),
            locked=locked_repo,
            io=NullIO(),
        )
        solver.provider.load_deferred(False)
        with solver.use_environment(self.env):
            ops = solver.solve()

        required_locked_packages = set(
            [op.package for op in ops if not op.skipped])

        if self.option("no-dev"):
            required_locked_packages = [
                p for p in locked_packages if p.category == "main"
            ]

        if package:
            pkg = None
            for locked in locked_packages:
                if package.lower() == locked.name:
                    pkg = locked
                    break

            if not pkg:
                raise ValueError("Package {} not found".format(package))

            if self.option("tree"):
                self.display_package_tree(self.io, pkg, locked_repo)

                return 0

            rows = [
                ["<info>name</>", " : <c1>{}</>".format(pkg.pretty_name)],
                [
                    "<info>version</>",
                    " : <b>{}</b>".format(pkg.pretty_version)
                ],
                ["<info>description</>", " : {}".format(pkg.description)],
            ]

            table.add_rows(rows)
            table.render(self.io)

            if pkg.requires:
                self.line("")
                self.line("<info>dependencies</info>")
                for dependency in pkg.requires:
                    self.line(" - <c1>{}</c1> <b>{}</b>".format(
                        dependency.pretty_name, dependency.pretty_constraint))

            return 0

        show_latest = self.option("latest")
        show_all = self.option("all")
        terminal = Terminal()
        width = terminal.width
        name_length = version_length = latest_length = 0
        latest_packages = {}
        latest_statuses = {}
        installed_repo = InstalledRepository.load(self.env)

        # Computing widths
        for locked in locked_packages:
            if locked not in required_locked_packages and not show_all:
                continue

            current_length = len(locked.pretty_name)
            if not self._io.output.supports_ansi():
                installed_status = self.get_installed_status(
                    locked, installed_repo)

                if installed_status == "not-installed":
                    current_length += 4

            if show_latest:
                latest = self.find_latest_package(locked, include_dev)
                if not latest:
                    latest = locked

                latest_packages[locked.pretty_name] = latest
                update_status = latest_statuses[
                    locked.pretty_name] = self.get_update_status(
                        latest, locked)

                if not self.option(
                        "outdated") or update_status != "up-to-date":
                    name_length = max(name_length, current_length)
                    version_length = max(
                        version_length,
                        len(
                            get_package_version_display_string(
                                locked, root=self.poetry.file.parent)),
                    )
                    latest_length = max(
                        latest_length,
                        len(
                            get_package_version_display_string(
                                latest, root=self.poetry.file.parent)),
                    )
            else:
                name_length = max(name_length, current_length)
                version_length = max(
                    version_length,
                    len(
                        get_package_version_display_string(
                            locked, root=self.poetry.file.parent)),
                )

        write_version = name_length + version_length + 3 <= width
        write_latest = name_length + version_length + latest_length + 3 <= width
        write_description = name_length + version_length + latest_length + 24 <= width

        for locked in locked_packages:
            color = "cyan"
            name = locked.pretty_name
            install_marker = ""
            if locked not in required_locked_packages:
                if not show_all:
                    continue

                color = "black;options=bold"
            else:
                installed_status = self.get_installed_status(
                    locked, installed_repo)
                if installed_status == "not-installed":
                    color = "red"

                    if not self._io.output.supports_ansi():
                        # Non installed in non decorated mode
                        install_marker = " (!)"

            if (show_latest and self.option("outdated")
                    and latest_statuses[locked.pretty_name] == "up-to-date"):
                continue

            line = "<fg={}>{:{}}{}</>".format(
                color, name, name_length - len(install_marker), install_marker)
            if write_version:
                line += " <b>{:{}}</b>".format(
                    get_package_version_display_string(
                        locked, root=self.poetry.file.parent),
                    version_length,
                )
            if show_latest:
                latest = latest_packages[locked.pretty_name]
                update_status = latest_statuses[locked.pretty_name]

                if write_latest:
                    color = "green"
                    if update_status == "semver-safe-update":
                        color = "red"
                    elif update_status == "update-possible":
                        color = "yellow"

                    line += " <fg={}>{:{}}</>".format(
                        color,
                        get_package_version_display_string(
                            latest, root=self.poetry.file.parent),
                        latest_length,
                    )

            if write_description:
                description = locked.description
                remaining = width - name_length - version_length - 4
                if show_latest:
                    remaining -= latest_length

                if len(locked.description) > remaining:
                    description = description[:remaining - 3] + "..."

                line += " " + description

            self.line(line)

    def display_package_tree(self, io, package, installed_repo):
        io.write("<c1>{}</c1>".format(package.pretty_name))
        description = ""
        if package.description:
            description = " " + package.description

        io.write_line(" <b>{}</b>{}".format(package.pretty_version,
                                            description))

        dependencies = package.requires
        dependencies = sorted(dependencies, key=lambda x: x.name)
        tree_bar = "├"
        j = 0
        total = len(dependencies)
        for dependency in dependencies:
            j += 1
            if j == total:
                tree_bar = "└"

            level = 1
            color = self.colors[level]
            info = "{tree_bar}── <{color}>{name}</{color}> {constraint}".format(
                tree_bar=tree_bar,
                color=color,
                name=dependency.name,
                constraint=dependency.pretty_constraint,
            )
            self._write_tree_line(io, info)

            tree_bar = tree_bar.replace("└", " ")
            packages_in_tree = [package.name, dependency.name]

            self._display_tree(io, dependency, installed_repo,
                               packages_in_tree, tree_bar, level + 1)

    def _display_tree(
        self,
        io,
        dependency,
        installed_repo,
        packages_in_tree,
        previous_tree_bar="├",
        level=1,
    ):
        previous_tree_bar = previous_tree_bar.replace("├", "│")

        dependencies = []
        for package in installed_repo.packages:
            if package.name == dependency.name:
                dependencies = package.requires

                break

        dependencies = sorted(dependencies, key=lambda x: x.name)
        tree_bar = previous_tree_bar + "   ├"
        i = 0
        total = len(dependencies)
        for dependency in dependencies:
            i += 1
            current_tree = packages_in_tree
            if i == total:
                tree_bar = previous_tree_bar + "   └"

            color_ident = level % len(self.colors)
            color = self.colors[color_ident]

            circular_warn = ""
            if dependency.name in current_tree:
                circular_warn = "(circular dependency aborted here)"

            info = "{tree_bar}── <{color}>{name}</{color}> {constraint} {warn}".format(
                tree_bar=tree_bar,
                color=color,
                name=dependency.name,
                constraint=dependency.pretty_constraint,
                warn=circular_warn,
            )
            self._write_tree_line(io, info)

            tree_bar = tree_bar.replace("└", " ")

            if dependency.name not in current_tree:
                current_tree.append(dependency.name)

                self._display_tree(io, dependency, installed_repo,
                                   current_tree, tree_bar, level + 1)

    def _write_tree_line(self, io, line):
        if not io.output.supports_ansi():
            line = line.replace("└", "`-")
            line = line.replace("├", "|-")
            line = line.replace("──", "-")
            line = line.replace("│", "|")

        io.write_line(line)

    def init_styles(self, io):
        from clikit.api.formatter import Style

        for color in self.colors:
            style = Style(color).fg(color)
            io.output.formatter.add_style(style)
            io.error_output.formatter.add_style(style)

    def find_latest_package(self, package, include_dev):
        from clikit.io import NullIO

        from poetry.puzzle.provider import Provider
        from poetry.version.version_selector import VersionSelector

        # find the latest version allowed in this pool
        if package.source_type in ("git", "file", "directory"):
            requires = self.poetry.package.requires
            if include_dev:
                requires = requires + self.poetry.package.dev_requires

            for dep in requires:
                if dep.name == package.name:
                    provider = Provider(self.poetry.package, self.poetry.pool,
                                        NullIO())

                    if dep.is_vcs():
                        return provider.search_for_vcs(dep)[0]
                    if dep.is_file():
                        return provider.search_for_file(dep)[0]
                    if dep.is_directory():
                        return provider.search_for_directory(dep)[0]

        name = package.name
        selector = VersionSelector(self.poetry.pool)

        return selector.find_best_candidate(
            name, ">={}".format(package.pretty_version))

    def get_update_status(self, latest, package):
        from poetry.core.semver import parse_constraint

        if latest.full_pretty_version == package.full_pretty_version:
            return "up-to-date"

        constraint = parse_constraint("^" + package.pretty_version)

        if latest.version and constraint.allows(latest.version):
            # It needs an immediate semver-compliant upgrade
            return "semver-safe-update"

        # it needs an upgrade but has potential BC breaks so is not urgent
        return "update-possible"

    def get_installed_status(self, locked, installed_repo):
        for package in installed_repo.packages:
            if locked.name == package.name:
                return "installed"

        return "not-installed"
コード例 #30
0
class DebugResolveCommand(Command):

    name = "resolve"
    description = "Debugs dependency resolution."

    arguments = [
        argument("package",
                 "The packages to resolve.",
                 optional=True,
                 multiple=True)
    ]
    options = [
        option(
            "extras",
            "E",
            "Extras to activate for the dependency.",
            flag=False,
            multiple=True,
        ),
        option("python",
               None,
               "Python version(s) to use for resolution.",
               flag=False),
        option("tree", None, "Display the dependency tree."),
        option("install", None,
               "Show what would be installed for the current system."),
    ]

    loggers = ["poetry.repositories.pypi_repository"]

    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 EnvManager

        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._io)

        ops = solver.solve()

        self.line("")
        self.line("Resolution results:")
        self.line("")

        if self.option("tree"):
            show_command = self.application.find("show")
            show_command.init_styles(self.io)

            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(self.io, pkg, repo)
                        break

            return 0

        env = EnvManager(self.poetry.config).get(self.poetry.file.parent)
        current_python_version = parse_constraint(".".join(
            str(v) for v in env.version_info))
        table = self.table([], style="borderless")
        rows = []
        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
            row = [
                "<info>{}</info>".format(pkg.name),
                "<b>{}</b>".format(pkg.version),
                "",
            ]

            if not pkg.marker.is_any():
                row[2] = str(pkg.marker)

            rows.append(row)

        table.set_rows(rows)
        table.render(self.io)

    def _determine_requirements(self,
                                requires):  # type: (List[str]) -> List[str]
        from poetry.semver import parse_constraint

        if not requires:
            return []

        requires = self._parse_name_version_pairs(requires)
        for requirement in requires:
            if "version" in requirement:
                parse_constraint(requirement["version"])

        return requires

    def _parse_name_version_pairs(self, pairs):  # type: (list) -> list
        result = []

        for i in range(len(pairs)):
            if pairs[i].startswith("git+https://"):
                url = pairs[i].lstrip("git+")
                rev = None
                if "@" in url:
                    url, rev = url.split("@")

                pair = {"name": url.split("/")[-1].rstrip(".git"), "git": url}
                if rev:
                    pair["rev"] = rev

                result.append(pair)

                continue

            pair = re.sub("^([^=: ]+)[=: ](.*)$", "\\1 \\2", pairs[i].strip())
            pair = pair.strip()

            if " " in pair:
                name, version = pair.split(" ", 2)
                result.append({"name": name, "version": version})
            else:
                result.append({"name": pair, "version": "*"})

        return result

    def _format_requirements(self, requirements):  # type: (List[str]) -> dict
        requires = {}
        requirements = self._determine_requirements(requirements)

        for requirement in requirements:
            name = requirement.pop("name")
            requires[name] = requirement

        return requires