231 lines
6.4 KiB
Python
231 lines
6.4 KiB
Python
|
# -*- coding: utf-8 -*-
|
||
|
from __future__ import unicode_literals, absolute_import
|
||
|
|
||
|
import json
|
||
|
from json import JSONEncoder
|
||
|
|
||
|
from . import filetypes, errors
|
||
|
|
||
|
|
||
|
class Dependency(object):
|
||
|
"""
|
||
|
|
||
|
"""
|
||
|
|
||
|
def __init__(self, name, specs, line, source="pypi", meta={}, extras=[],
|
||
|
line_numbers=None, index_server=None, hashes=(),
|
||
|
dependency_type=None, section=None):
|
||
|
"""
|
||
|
|
||
|
:param name:
|
||
|
:param specs:
|
||
|
:param line:
|
||
|
:param source:
|
||
|
:param extras:
|
||
|
:param line_numbers:
|
||
|
:param index_server:
|
||
|
:param hashes:
|
||
|
:param dependency_type:
|
||
|
"""
|
||
|
self.name = name
|
||
|
self.key = name.lower().replace("_", "-")
|
||
|
self.specs = specs
|
||
|
self.line = line
|
||
|
self.source = source
|
||
|
self.meta = meta
|
||
|
self.line_numbers = line_numbers
|
||
|
self.index_server = index_server
|
||
|
self.hashes = hashes
|
||
|
self.dependency_type = dependency_type
|
||
|
self.extras = extras
|
||
|
self.section = section
|
||
|
|
||
|
def __str__(self): # pragma: no cover
|
||
|
"""
|
||
|
|
||
|
:return:
|
||
|
"""
|
||
|
return "Dependency({name}, {specs}, {line})".format(
|
||
|
name=self.name,
|
||
|
specs=self.specs,
|
||
|
line=self.line
|
||
|
)
|
||
|
|
||
|
def serialize(self):
|
||
|
"""
|
||
|
|
||
|
:return:
|
||
|
"""
|
||
|
return {
|
||
|
"name": self.name,
|
||
|
"specs": self.specs,
|
||
|
"line": self.line,
|
||
|
"source": self.source,
|
||
|
"meta": self.meta,
|
||
|
"line_numbers": self.line_numbers,
|
||
|
"index_server": self.index_server,
|
||
|
"hashes": self.hashes,
|
||
|
"dependency_type": self.dependency_type,
|
||
|
"extras": self.extras,
|
||
|
"section": self.section
|
||
|
}
|
||
|
|
||
|
@classmethod
|
||
|
def deserialize(cls, d):
|
||
|
"""
|
||
|
|
||
|
:param d:
|
||
|
:return:
|
||
|
"""
|
||
|
return cls(**d)
|
||
|
|
||
|
@property
|
||
|
def full_name(self):
|
||
|
"""
|
||
|
|
||
|
:return:
|
||
|
"""
|
||
|
if self.extras:
|
||
|
return "{}[{}]".format(self.name, ",".join(self.extras))
|
||
|
return self.name
|
||
|
|
||
|
|
||
|
class DparseJSONEncoder(JSONEncoder):
|
||
|
def default(self, o):
|
||
|
from pipenv.vendor.packaging.specifiers import SpecifierSet
|
||
|
|
||
|
if isinstance(o, SpecifierSet):
|
||
|
return str(o)
|
||
|
if isinstance(o, set):
|
||
|
return list(o)
|
||
|
|
||
|
return JSONEncoder.default(self, o)
|
||
|
|
||
|
|
||
|
class DependencyFile(object):
|
||
|
"""
|
||
|
|
||
|
"""
|
||
|
|
||
|
def __init__(self, content, path=None, sha=None, file_type=None,
|
||
|
marker=((), ()), parser=None, resolve=False):
|
||
|
"""
|
||
|
|
||
|
:param content:
|
||
|
:param path:
|
||
|
:param sha:
|
||
|
:param marker:
|
||
|
:param file_type:
|
||
|
:param parser:
|
||
|
"""
|
||
|
self.content = content
|
||
|
self.file_type = file_type
|
||
|
self.path = path
|
||
|
self.sha = sha
|
||
|
self.marker = marker
|
||
|
|
||
|
self.dependencies = []
|
||
|
self.resolved_files = []
|
||
|
self.is_valid = False
|
||
|
self.file_marker, self.line_marker = marker
|
||
|
|
||
|
if parser:
|
||
|
self.parser = parser
|
||
|
else:
|
||
|
from . import parser as parser_class
|
||
|
if file_type is not None:
|
||
|
if file_type == filetypes.requirements_txt:
|
||
|
self.parser = parser_class.RequirementsTXTParser
|
||
|
elif file_type == filetypes.tox_ini:
|
||
|
self.parser = parser_class.ToxINIParser
|
||
|
elif file_type == filetypes.conda_yml:
|
||
|
self.parser = parser_class.CondaYMLParser
|
||
|
elif file_type == filetypes.pipfile:
|
||
|
self.parser = parser_class.PipfileParser
|
||
|
elif file_type == filetypes.pipfile_lock:
|
||
|
self.parser = parser_class.PipfileLockParser
|
||
|
elif file_type == filetypes.setup_cfg:
|
||
|
self.parser = parser_class.SetupCfgParser
|
||
|
elif file_type == filetypes.poetry_lock:
|
||
|
self.parser = parser_class.PoetryLockParser
|
||
|
|
||
|
elif path is not None:
|
||
|
if path.endswith((".txt", ".in")):
|
||
|
self.parser = parser_class.RequirementsTXTParser
|
||
|
elif path.endswith(".yml"):
|
||
|
self.parser = parser_class.CondaYMLParser
|
||
|
elif path.endswith(".ini"):
|
||
|
self.parser = parser_class.ToxINIParser
|
||
|
elif path.endswith("Pipfile"):
|
||
|
self.parser = parser_class.PipfileParser
|
||
|
elif path.endswith("Pipfile.lock"):
|
||
|
self.parser = parser_class.PipfileLockParser
|
||
|
elif path.endswith("setup.cfg"):
|
||
|
self.parser = parser_class.SetupCfgParser
|
||
|
elif path.endswith(filetypes.poetry_lock):
|
||
|
self.parser = parser_class.PoetryLockParser
|
||
|
|
||
|
if not hasattr(self, "parser"):
|
||
|
raise errors.UnknownDependencyFileError
|
||
|
|
||
|
self.parser = self.parser(self, resolve=resolve)
|
||
|
|
||
|
@property
|
||
|
def resolved_dependencies(self):
|
||
|
deps = self.dependencies.copy()
|
||
|
|
||
|
for d in self.resolved_files:
|
||
|
if isinstance(d, DependencyFile):
|
||
|
deps.extend(d.resolved_dependencies)
|
||
|
|
||
|
return deps
|
||
|
|
||
|
def serialize(self):
|
||
|
"""
|
||
|
|
||
|
:return:
|
||
|
"""
|
||
|
return {
|
||
|
"file_type": self.file_type,
|
||
|
"content": self.content,
|
||
|
"path": self.path,
|
||
|
"sha": self.sha,
|
||
|
"dependencies": [dep.serialize() for dep in self.dependencies],
|
||
|
"resolved_dependencies": [dep.serialize() for dep in
|
||
|
self.resolved_dependencies]
|
||
|
}
|
||
|
|
||
|
@classmethod
|
||
|
def deserialize(cls, d):
|
||
|
"""
|
||
|
|
||
|
:param d:
|
||
|
:return:
|
||
|
"""
|
||
|
dependencies = [Dependency.deserialize(dep) for dep in
|
||
|
d.pop("dependencies", [])]
|
||
|
instance = cls(**d)
|
||
|
instance.dependencies = dependencies
|
||
|
return instance
|
||
|
|
||
|
def json(self): # pragma: no cover
|
||
|
"""
|
||
|
|
||
|
:return:
|
||
|
"""
|
||
|
return json.dumps(self.serialize(), indent=2, cls=DparseJSONEncoder)
|
||
|
|
||
|
def parse(self):
|
||
|
"""
|
||
|
|
||
|
:return:
|
||
|
"""
|
||
|
if self.parser.is_marked_file:
|
||
|
self.is_valid = False
|
||
|
return self
|
||
|
self.parser.parse()
|
||
|
|
||
|
self.is_valid = len(self.dependencies) > 0 or len(
|
||
|
self.resolved_files) > 0
|
||
|
return self
|