def test_setup_reader_read_first_level_setup_call_with_variables(setup):
    result = SetupReader.read_from_directory(setup("requests"))

    expected_name = None
    expected_version = None
    expected_install_requires = [
        "chardet>=3.0.2,<3.1.0",
        "idna>=2.5,<2.8",
        "urllib3>=1.21.1,<1.25",
        "certifi>=2017.4.17",
    ]
    expected_extras_require = {
        "security":
        ["pyOpenSSL >= 0.14", "cryptography>=1.3.4", "idna>=2.0.0"],
        "socks": ["PySocks>=1.5.6, !=1.5.7"],
        'socks:sys_platform == "win32" and python_version == "2.7"':
        ["win_inet_pton"],
    }
    expected_python_requires = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"

    assert expected_name == result["name"]
    assert expected_version == result["version"]
    assert expected_install_requires == result["install_requires"]
    assert expected_extras_require == result["extras_require"]
    assert expected_python_requires == result["python_requires"]
Beispiel #2
0
def test_setup_reader_read_first_level_setup_call_with_direct_types(setup):
    result = SetupReader.read_from_directory(setup("flask"))

    expected_name = "Flask"
    expected_version = None
    expected_install_requires = [
        "Werkzeug>=0.14",
        "Jinja2>=2.10",
        "itsdangerous>=0.24",
        "click>=5.1",
    ]
    expected_extras_require = {
        "dotenv": ["python-dotenv"],
        "dev": [
            "pytest>=3",
            "coverage",
            "tox",
            "sphinx",
            "pallets-sphinx-themes",
            "sphinxcontrib-log-cabinet",
        ],
        "docs": ["sphinx", "pallets-sphinx-themes", "sphinxcontrib-log-cabinet"],
    }
    expected_python_requires = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*"

    assert expected_name == result["name"]
    assert expected_version == result["version"]
    assert expected_install_requires == result["install_requires"]
    assert expected_extras_require == result["extras_require"]
    assert expected_python_requires == result["python_requires"]
Beispiel #3
0
def test_setup_reader_setuptools(setup: Callable[[str], str]):
    result = SetupReader.read_from_directory(setup("setuptools_setup"))

    expected_name = "my_package"
    expected_version = "0.1.2"

    assert result["name"] == expected_name
    assert result["version"] == expected_version
def test_setup_reader_setuptools(setup):
    result = SetupReader.read_from_directory(setup("setuptools_setup"))

    expected_name = "my_package"
    expected_version = "0.1.2"

    assert expected_name == result["name"]
    assert expected_version == result["version"]
Beispiel #5
0
    def from_setup_files(cls, path):  # type: (Path) -> PackageInfo
        """
        Mechanism to parse package information from a `setup.[py|cfg]` file. This uses the implementation
        at `poetry.utils.setup_reader.SetupReader` in order to parse the file. This is not reliable for
        complex setup files and should only attempted as a fallback.

        :param path: Path to `setup.py` file
        """
        if not cls.has_setup_files(path):
            raise PackageInfoError(
                path, "No setup files (setup.py, setup.cfg) was found."
            )

        try:
            result = SetupReader.read_from_directory(path)
        except Exception as e:
            raise PackageInfoError(path, e)

        python_requires = result["python_requires"]
        if python_requires is None:
            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"

        requirements = parse_requires(requires)

        info = cls(
            name=result.get("name"),
            version=result.get("version"),
            summary=result.get("description", ""),
            requires_dist=requirements or None,
            requires_python=python_requires,
        )

        if not (info.name and info.version) and not info.requires_dist:
            # there is nothing useful here
            raise PackageInfoError(
                path,
                "No core metadata (name, version, requires-dist) could be retrieved.",
            )

        return info
Beispiel #6
0
def test_setup_reader_read_setup_kwargs(setup):
    result = SetupReader.read_from_directory(setup("pendulum"))

    expected_name = "pendulum"
    expected_version = "2.0.4"
    expected_install_requires = ["python-dateutil>=2.6,<3.0", "pytzdata>=2018.3"]
    expected_extras_require = {':python_version < "3.5"': ["typing>=3.6,<4.0"]}
    expected_python_requires = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"

    assert expected_name == result["name"]
    assert expected_version == result["version"]
    assert expected_install_requires == result["install_requires"]
    assert expected_extras_require == result["extras_require"]
    assert expected_python_requires == result["python_requires"]
def test_setup_reader_read_setup_call_in_main(setup):
    result = SetupReader.read_from_directory(setup("pyyaml"))

    expected_name = "PyYAML"
    expected_version = "3.13"
    expected_install_requires = []
    expected_extras_require = {}
    expected_python_requires = None

    assert expected_name == result["name"]
    assert expected_version == result["version"]
    assert expected_install_requires == result["install_requires"]
    assert expected_extras_require == result["extras_require"]
    assert expected_python_requires == result["python_requires"]
def test_setup_reader_read_extras_require_with_variables(setup):
    result = SetupReader.read_from_directory(setup("extras_require_with_vars"))

    expected_name = "extras_require_with_vars"
    expected_version = "0.0.1"
    expected_install_requires = []
    expected_extras_require = {"test": ["pytest"]}
    expected_python_requires = None

    assert expected_name == result["name"]
    assert expected_version == result["version"]
    assert expected_install_requires == result["install_requires"]
    assert expected_extras_require == result["extras_require"]
    assert expected_python_requires == result["python_requires"]
Beispiel #9
0
def test_setup_reader_read_setup_cfg(setup):
    result = SetupReader.read_from_directory(setup("with-setup-cfg"))

    expected_name = "with-setup-cfg"
    expected_version = "1.2.3"
    expected_install_requires = ["six", "tomlkit"]
    expected_extras_require = {
        "validation": ["cerberus"],
        "tests": ["pytest", "pytest-xdist", "pytest-cov"],
    }
    expected_python_requires = ">=2.6,!=3.0,!=3.1,!=3.2,!=3.3"

    assert expected_name == result["name"]
    assert expected_version == result["version"]
    assert expected_install_requires == result["install_requires"]
    assert expected_extras_require == result["extras_require"]
    assert expected_python_requires == result["python_requires"]
Beispiel #10
0
def test_setup_reader_read_sub_level_setup_call_with_direct_types(setup):
    result = SetupReader.read_from_directory(setup("sqlalchemy"))

    expected_name = "SQLAlchemy"
    expected_version = None
    expected_install_requires = []
    expected_extras_require = {
        "mysql": ["mysqlclient"],
        "pymysql": ["pymysql"],
        "postgresql": ["psycopg2"],
        "postgresql_pg8000": ["pg8000"],
        "postgresql_psycopg2cffi": ["psycopg2cffi"],
        "oracle": ["cx_oracle"],
        "mssql_pyodbc": ["pyodbc"],
        "mssql_pymssql": ["pymssql"],
    }

    assert expected_name == result["name"]
    assert expected_version == result["version"]
    assert expected_install_requires == result["install_requires"]
    assert expected_extras_require == result["extras_require"]
    assert result["python_requires"] is None
Beispiel #11
0
    def _inspect_sdist_with_setup(self, sdist_dir):
        info = {"requires_python": None, "requires_dist": None}

        result = SetupReader.read_from_directory(sdist_dir)
        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"

        info["requires_dist"] = parse_requires(requires)
        info["requires_python"] = result["python_requires"]

        return info
Beispiel #12
0
    def get_package_from_directory(
            cls,
            directory,
            name=None):  # type: (Path, Optional[str]) -> Package
        supports_poetry = False
        pyproject = directory.joinpath("pyproject.toml")
        if pyproject.exists():
            pyproject = TomlFile(pyproject)
            pyproject_content = pyproject.read()
            supports_poetry = ("tool" in pyproject_content
                               and "poetry" in pyproject_content["tool"])

        if supports_poetry:
            poetry = Factory().create_poetry(directory)

            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(directory))

            try:
                cwd = directory
                venv = EnvManager().get(cwd)
                venv.run("python", "setup.py", "egg_info")
            except EnvCommandError:
                result = SetupReader.read_from_directory(directory)
                if not result["name"]:
                    # The name could not be determined
                    # We use the dependency name
                    result["name"] = 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(
                            directory))

                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(directory), "**", "*.egg-info"),
                            recursive=True,
                        ))
                else:
                    egg_info = next(directory.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(encoding="utf-8") 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:
                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

        if name and 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(name, package.name))

        package.source_type = "directory"
        package.source_url = directory.as_posix()

        return package
Beispiel #13
0
    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]
Beispiel #14
0
    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,
        )
Beispiel #15
0
def test_setup_reader_read_setup_cfg_with_attr(setup):
    with pytest.raises(ParseVersionError):
        SetupReader.read_from_directory(setup("with-setup-cfg-attr"))
Beispiel #16
0
def test_setup_reader_read_setup_cfg_with_attr(setup: Callable[[str], str]):
    with pytest.raises(InvalidVersion):
        SetupReader.read_from_directory(setup("with-setup-cfg-attr"))
Beispiel #17
0
    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]