def to_package( self, name=None, extras=None, root_dir=None ): # type: (Optional[str], Optional[List[str]], Optional[Path]) -> Package """ Create a new `poetry.core.packages.package.Package` instance using metadata from this instance. :param name: Name to use for the package, if not specified name from this instance is used. :param extras: Extras to activate for this package. :param root_dir: Optional root directory to use for the package. If set, dependency strings will be parsed relative to this directory. """ name = name or self.name if not self.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(name) ) package = Package(name=name, version=self.version) package.description = self.summary package.root_dir = root_dir package.python_versions = self.requires_python or "*" package.files = self.files for req in self.requires_dist or []: try: # Attempt to parse the PEP-508 requirement string dependency = dependency_from_pep_508(req, relative_to=root_dir) except InvalidMarker: # Invalid marker, We strip the markers hoping for the best req = req.split(";")[0] dependency = dependency_from_pep_508(req, relative_to=root_dir) except ValueError: # Likely unable to parse constraint so we skip it self._log( "Invalid constraint ({}) found in {}-{} dependencies, " "skipping".format(req, package.name, package.version), level="warning", ) continue if dependency.in_extras: # this dependency is required by an extra package for extra in dependency.in_extras: if extra not in package.extras: # this is the first time we encounter this extra for this package package.extras[extra] = [] # Activate extra dependencies if specified if extras and extra in extras: dependency.activate() package.extras[extra].append(dependency) if not dependency.is_optional() or dependency.is_activated(): # we skip add only if the dependency is option and was not activated as part of an extra package.requires.append(dependency) return package
def dependencies(self): if self._dependencies is None: # avoid circular dependency when loading DirectoryDependency from poetry.core.packages import DirectoryDependency from poetry.core.packages import FileDependency from poetry.core.packages import dependency_from_pep_508 self._dependencies = [] for requirement in self.requires: dependency = None try: dependency = dependency_from_pep_508(requirement) except ValueError: # PEP 517 requires can be path if not PEP 508 path = Path(requirement) try: if path.is_file(): dependency = FileDependency(name=canonicalize_name( path.name), path=path) elif path.is_dir(): dependency = DirectoryDependency( name=canonicalize_name(path.name), path=path) except OSError: # compatibility Python < 3.8 # https://docs.python.org/3/library/pathlib.html#methods pass if dependency is None: # skip since we could not determine requirement continue self._dependencies.append(dependency) return self._dependencies
def test_dependency_from_pep_508_with_python_full_version_pep440_compatible_release_tilde(): name = 'pathlib2 ; python_version ~= "3.4" or python_version < "3"' dep = dependency_from_pep_508(name) assert dep.name == "pathlib2" assert str(dep.constraint) == "*" assert dep.python_versions == "~=3.4 || <3"
def test_to_pep_508_in_extras_parsed(): dependency = dependency_from_pep_508('foo[bar] (>=1.23,<2.0) ; extra == "baz"') result = dependency.to_pep_508() assert result == 'foo[bar] (>=1.23,<2.0); extra == "baz"' result = dependency.to_pep_508(with_extras=False) assert result == "foo[bar] (>=1.23,<2.0)"
def test_dependency_from_pep_508_with_platform(): name = 'requests (==2.18.0); sys_platform == "win32" or sys_platform == "darwin"' dep = dependency_from_pep_508(name) assert dep.name == "requests" assert str(dep.constraint) == "2.18.0" assert dep.extras == [] assert dep.python_versions == "*" assert str(dep.marker) == 'sys_platform == "win32" or sys_platform == "darwin"'
def _test_directory_dependency_pep_508(name, path, pep_508_input, pep_508_output=None): dep = dependency_from_pep_508(pep_508_input, relative_to=Path(__file__).parent) assert dep.is_directory() assert dep.name == name assert dep.path == path assert dep.to_pep_508() == pep_508_output or pep_508_input
def _test_file_dependency_pep_508( mocker, name, path, pep_508_input, pep_508_output=None ): mocker.patch.object(Path, "exists").return_value = True mocker.patch.object(Path, "is_file").return_value = True dep = dependency_from_pep_508(pep_508_input, relative_to=Path(__file__).parent) assert dep.is_file() assert dep.name == name assert dep.path == path assert dep.to_pep_508() == pep_508_output or pep_508_input
def test_dependency_from_pep_508_with_git_url_and_comment_and_extra(): name = ( "poetry @ git+https://github.com/python-poetry/poetry.git@b;ar;#egg=poetry" ' ; extra == "foo;"') dep = dependency_from_pep_508(name) assert "poetry" == dep.name assert dep.is_vcs() assert "git" == dep.vcs assert "https://github.com/python-poetry/poetry.git" == dep.source assert "b;ar;" == dep.reference assert dep.in_extras == ["foo;"]
def test_dependency_from_pep_508_should_not_produce_empty_constraints_for_correct_markers( ): name = 'pytest-mypy; python_implementation != "PyPy" and python_version <= "3.10" and python_version > "3"' dep = dependency_from_pep_508(name) assert dep.name == "pytest-mypy" assert str(dep.constraint) == "*" assert dep.python_versions == "<=3.10 >3" assert dep.python_constraint.allows(Version.parse("3.6")) assert dep.python_constraint.allows(Version.parse("3.10")) assert not dep.python_constraint.allows(Version.parse("3")) assert dep.python_constraint.allows(Version.parse("3.0.1")) assert ( str(dep.marker) == 'platform_python_implementation != "PyPy" and python_version <= "3.10" and python_version > "3"' )
def to_package( self, name=None, extras=None, root_dir=None ): # type: (Optional[str], Optional[List[str]], Optional[Path]) -> Package """ Create a new `poetry.core.packages.package.Package` instance using metadata from this instance. :param name: Name to use for the package, if not specified name from this instance is used. :param extras: Extras to activate for this package. :param root_dir: Optional root directory to use for the package. If set, dependency strings will be parsed relative to this directory. """ name = name or self.name if not self.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(name) ) package = Package( name=name, version=self.version, source_type=self._source_type, source_url=self._source_url, source_reference=self._source_reference, ) package.description = self.summary package.root_dir = root_dir package.python_versions = self.requires_python or "*" package.files = self.files if root_dir or (self._source_type in {"directory"} and self._source_url): # this is a local poetry project, this means we can extract "richer" requirement information # eg: development requirements etc. poetry_package = self._get_poetry_package(path=root_dir or self._source_url) if poetry_package: package.extras = poetry_package.extras package.requires = poetry_package.requires return package seen_requirements = set() for req in self.requires_dist or []: try: # Attempt to parse the PEP-508 requirement string dependency = dependency_from_pep_508(req, relative_to=root_dir) except InvalidMarker: # Invalid marker, We strip the markers hoping for the best req = req.split(";")[0] dependency = dependency_from_pep_508(req, relative_to=root_dir) except ValueError: # Likely unable to parse constraint so we skip it self._log( "Invalid constraint ({}) found in {}-{} dependencies, " "skipping".format(req, package.name, package.version), level="warning", ) continue if dependency.in_extras: # this dependency is required by an extra package for extra in dependency.in_extras: if extra not in package.extras: # this is the first time we encounter this extra for this package package.extras[extra] = [] package.extras[extra].append(dependency) req = dependency.to_pep_508(with_extras=True) if req not in seen_requirements: package.requires.append(dependency) seen_requirements.add(req) return package