Esempio n. 1
0
    def __call__(
        self,
        requirements_relpath: str = "requirements.txt",
        *,
        module_mapping: Optional[Mapping[str, Iterable[str]]] = None,
        type_stubs_module_mapping: Optional[Mapping[str, Iterable[str]]] = None,
    ) -> None:
        """
        :param requirements_relpath: The relpath from this BUILD file to the requirements file.
            Defaults to a `requirements.txt` file sibling to the BUILD file.
        :param module_mapping: a mapping of requirement names to a list of the modules they provide.
            For example, `{"ansicolors": ["colors"]}`. Any unspecified requirements will use the
            requirement name as the default module, e.g. "Django" will default to
            `modules=["django"]`.
        """
        req_file_tgt = self._parse_context.create_object(
            "_python_requirements_file",
            name=requirements_relpath.replace(os.path.sep, "_"),
            sources=[requirements_relpath],
        )
        requirements_dep = f":{req_file_tgt.name}"

        normalized_module_mapping = normalize_module_mapping(module_mapping)
        normalized_type_stubs_module_mapping = normalize_module_mapping(type_stubs_module_mapping)

        req_file = Path(get_buildroot(), self._parse_context.rel_path, requirements_relpath)
        requirements = parse_requirements_file(
            req_file.read_text(), rel_path=str(req_file.relative_to(get_buildroot()))
        )

        grouped_requirements = groupby(requirements, lambda parsed_req: parsed_req.project_name)

        for project_name, parsed_reqs_ in grouped_requirements:
            normalized_proj_name = canonicalize_project_name(project_name)
            req_module_mapping = (
                {normalized_proj_name: normalized_module_mapping[normalized_proj_name]}
                if normalized_proj_name in normalized_module_mapping
                else {}
            )
            req_stubs_mapping = (
                {normalized_proj_name: normalized_type_stubs_module_mapping[normalized_proj_name]}
                if normalized_proj_name in normalized_type_stubs_module_mapping
                else {}
            )
            self._parse_context.create_object(
                "python_requirement_library",
                name=project_name,
                requirements=list(parsed_reqs_),
                module_mapping=req_module_mapping,
                type_stubs_module_mapping=req_stubs_mapping,
                dependencies=[requirements_dep],
            )
Esempio n. 2
0
    def __call__(
        self,
        *,
        source: str = "requirements.txt",
        module_mapping: Mapping[str, Iterable[str]] | None = None,
        type_stubs_module_mapping: Mapping[str, Iterable[str]] | None = None,
        overrides: OVERRIDES_TYPE = None,
    ) -> None:
        """
        :param module_mapping: a mapping of requirement names to a list of the modules they provide.
            For example, `{"ansicolors": ["colors"]}`. Any unspecified requirements will use the
            requirement name as the default module, e.g. "Django" will default to
            `modules=["django"]`.
        """
        req_file_tgt = self._parse_context.create_object(
            "_python_requirements_file",
            name=source.replace(os.path.sep, "_"),
            sources=[source],
        )
        requirements_dep = f":{req_file_tgt.name}"

        normalized_module_mapping = normalize_module_mapping(module_mapping)
        normalized_type_stubs_module_mapping = normalize_module_mapping(
            type_stubs_module_mapping)

        req_file = Path(get_buildroot(), self._parse_context.rel_path, source)
        requirements = parse_requirements_file(
            req_file.read_text(),
            rel_path=str(req_file.relative_to(get_buildroot())))

        dependencies_overrides = flatten_overrides_to_dependency_field(
            overrides,
            macro_name="python_requirements",
            build_file_dir=self._parse_context.rel_path)
        grouped_requirements = groupby(
            requirements, lambda parsed_req: parsed_req.project_name)

        for project_name, parsed_reqs_ in grouped_requirements:
            normalized_proj_name = canonicalize_project_name(project_name)
            self._parse_context.create_object(
                "python_requirement",
                name=project_name,
                requirements=list(parsed_reqs_),
                modules=normalized_module_mapping.get(normalized_proj_name),
                type_stub_modules=normalized_type_stubs_module_mapping.get(
                    normalized_proj_name),
                dependencies=[
                    requirements_dep,
                    *dependencies_overrides.get(normalized_proj_name, []),
                ],
            )
Esempio n. 3
0
    def __call__(
        self,
        pyproject_toml_relpath: str = "pyproject.toml",
        *,
        module_mapping: Optional[Mapping[str, Iterable[str]]] = None,
        type_stubs_module_mapping: Optional[Mapping[str,
                                                    Iterable[str]]] = None,
    ) -> None:
        """
        :param pyproject_toml_relpath: The relpath from this BUILD file to the requirements file.
            Defaults to a `requirements.txt` file sibling to the BUILD file.
        :param module_mapping: a mapping of requirement names to a list of the modules they provide.
            For example, `{"ansicolors": ["colors"]}`. Any unspecified requirements will use the
            requirement name as the default module, e.g. "Django" will default to
            `modules=["django"]`.
        """
        req_file_tgt = self._parse_context.create_object(
            "_python_requirements_file",
            name=pyproject_toml_relpath.replace(os.path.sep, "_"),
            sources=[pyproject_toml_relpath],
        )
        requirements_dep = f":{req_file_tgt.name}"

        normalized_module_mapping = normalize_module_mapping(module_mapping)
        normalized_type_stubs_module_mapping = normalize_module_mapping(
            type_stubs_module_mapping)

        requirements = parse_pyproject_toml(
            PyProjectToml.create(self._parse_context, pyproject_toml_relpath))
        for parsed_req in requirements:
            normalized_proj_name = canonicalize_project_name(
                parsed_req.project_name)
            req_module_mapping = ({
                normalized_proj_name:
                normalized_module_mapping[normalized_proj_name]
            } if normalized_proj_name in normalized_module_mapping else {})
            req_stubs_mapping = ({
                normalized_proj_name:
                normalized_type_stubs_module_mapping[normalized_proj_name]
            } if normalized_proj_name in normalized_type_stubs_module_mapping
                                 else {})

            self._parse_context.create_object(
                "python_requirement_library",
                name=parsed_req.project_name,
                requirements=[parsed_req],
                module_mapping=req_module_mapping,
                type_stubs_module_mapping=req_stubs_mapping,
                dependencies=[requirements_dep],
            )
    def __call__(
        self,
        *,
        source: str = "pyproject.toml",
        module_mapping: Mapping[str, Iterable[str]] | None = None,
        type_stubs_module_mapping: Mapping[str, Iterable[str]] | None = None,
        overrides: OVERRIDES_TYPE = None,
    ) -> None:
        """
        :param module_mapping: a mapping of requirement names to a list of the modules they provide.
            For example, `{"ansicolors": ["colors"]}`. Any unspecified requirements will use the
            requirement name as the default module, e.g. "Django" will default to
            `modules=["django"]`.
        """
        req_file_tgt = self._parse_context.create_object(
            TargetGeneratorSourcesHelperTarget.alias,
            name=source.replace(os.path.sep, "_"),
            sources=[source],
        )
        requirements_dep = f":{req_file_tgt.name}"

        normalized_module_mapping = normalize_module_mapping(module_mapping)
        normalized_type_stubs_module_mapping = normalize_module_mapping(
            type_stubs_module_mapping)

        dependencies_overrides = flatten_overrides_to_dependency_field(
            overrides,
            macro_name="python_requirements",
            build_file_dir=self._parse_context.rel_path)

        requirements = parse_pyproject_toml(
            PyProjectToml.deprecated_macro_create(self._parse_context, source))
        for parsed_req in requirements:
            normalized_proj_name = canonicalize_project_name(
                parsed_req.project_name)
            self._parse_context.create_object(
                "python_requirement",
                name=parsed_req.project_name,
                requirements=[parsed_req],
                modules=normalized_module_mapping.get(normalized_proj_name),
                type_stub_modules=normalized_type_stubs_module_mapping.get(
                    normalized_proj_name),
                dependencies=[
                    requirements_dep,
                    *dependencies_overrides.get(normalized_proj_name, []),
                ],
            )
Esempio n. 5
0
    def __call__(
        self,
        requirements_relpath: str = "Pipfile.lock",
        module_mapping: Optional[Mapping[str, Iterable[str]]] = None,
        type_stubs_module_mapping: Optional[Mapping[str,
                                                    Iterable[str]]] = None,
        pipfile_target: Optional[str] = None,
    ) -> None:
        """
        :param requirements_relpath: The relpath from this BUILD file to the requirements file.
            Defaults to a `Pipfile.lock` file sibling to the BUILD file.
        :param module_mapping: a mapping of requirement names to a list of the modules they provide.
            For example, `{"ansicolors": ["colors"]}`. Any unspecified requirements will use the
            requirement name as the default module, e.g. "Django" will default to
            `modules=["django"]`.
        :param pipfile_target: a `_python_requirements_file` target to provide for cache invalidation
        if the requirements_relpath value is not in the current rel_path
        """

        requirements_path = Path(get_buildroot(), self._parse_context.rel_path,
                                 requirements_relpath)
        lock_info = json.loads(requirements_path.read_text())

        if pipfile_target:
            requirements_dep = pipfile_target
        else:
            requirements_file_target_name = requirements_relpath
            self._parse_context.create_object(
                "_python_requirements_file",
                name=requirements_file_target_name,
                sources=[requirements_relpath],
            )
            requirements_dep = f":{requirements_file_target_name}"

        normalized_module_mapping = normalize_module_mapping(module_mapping)
        normalized_type_stubs_module_mapping = normalize_module_mapping(
            type_stubs_module_mapping)

        requirements = {
            **lock_info.get("default", {}),
            **lock_info.get("develop", {})
        }
        for req, info in requirements.items():
            extras = [x for x in info.get("extras", [])]
            extras_str = f"[{','.join(extras)}]" if extras else ""
            req_str = f"{req}{extras_str}{info.get('version','')}"
            if info.get("markers"):
                req_str += f";{info['markers']}"

            parsed_req = Requirement.parse(req_str)

            normalized_proj_name = canonicalize_project_name(
                parsed_req.project_name)
            req_module_mapping = ({
                normalized_proj_name:
                normalized_module_mapping[normalized_proj_name]
            } if normalized_proj_name in normalized_module_mapping else {})
            req_stubs_mapping = ({
                normalized_proj_name:
                normalized_type_stubs_module_mapping[normalized_proj_name]
            } if normalized_proj_name in normalized_type_stubs_module_mapping
                                 else {})

            self._parse_context.create_object(
                "python_requirement_library",
                name=parsed_req.project_name,
                requirements=[parsed_req],
                dependencies=[requirements_dep],
                module_mapping=req_module_mapping,
                type_stubs_module_mapping=req_stubs_mapping,
            )
Esempio n. 6
0
 def compute_value(  # type: ignore[override]
         cls, raw_value: Dict[str, Iterable[str]],
         address: Address) -> FrozenDict[str, Tuple[str, ...]]:
     value_or_default = super().compute_value(raw_value, address)
     return normalize_module_mapping(value_or_default)
Esempio n. 7
0
def test_normalize_module_mapping(
    raw_value: dict[str, Iterable[str]] | None, expected: dict[str, tuple[str, ...]]
) -> None:
    assert normalize_module_mapping(raw_value) == FrozenDict(expected)
Esempio n. 8
0
    def __call__(
        self,
        *,
        source: str = "Pipfile.lock",
        module_mapping: Mapping[str, Iterable[str]] | None = None,
        type_stubs_module_mapping: Mapping[str, Iterable[str]] | None = None,
        pipfile_target: str | None = None,
        overrides: OVERRIDES_TYPE = None,
    ) -> None:
        """
        :param module_mapping: a mapping of requirement names to a list of the modules they provide.
            For example, `{"ansicolors": ["colors"]}`. Any unspecified requirements will use the
            requirement name as the default module, e.g. "Django" will default to
            `modules=["django"]`.
        :param pipfile_target: a `_python_requirements_file` target to provide for cache invalidation
        if the requirements_relpath value is not in the current rel_path
        """
        requirements_path = Path(get_buildroot(), self._parse_context.rel_path,
                                 source)
        lock_info = json.loads(requirements_path.read_text())

        if pipfile_target:
            requirements_dep = pipfile_target
        else:
            requirements_file_target_name = source
            self._parse_context.create_object(
                "_python_requirements_file",
                name=requirements_file_target_name,
                sources=[source],
            )
            requirements_dep = f":{requirements_file_target_name}"

        normalized_module_mapping = normalize_module_mapping(module_mapping)
        normalized_type_stubs_module_mapping = normalize_module_mapping(
            type_stubs_module_mapping)

        dependencies_overrides = flatten_overrides_to_dependency_field(
            overrides,
            macro_name="python_requirements",
            build_file_dir=self._parse_context.rel_path)

        requirements = {
            **lock_info.get("default", {}),
            **lock_info.get("develop", {})
        }
        for req, info in requirements.items():
            extras = [x for x in info.get("extras", [])]
            extras_str = f"[{','.join(extras)}]" if extras else ""
            req_str = f"{req}{extras_str}{info.get('version','')}"
            if info.get("markers"):
                req_str += f";{info['markers']}"

            parsed_req = PipRequirement.parse(req_str)
            normalized_proj_name = canonicalize_project_name(
                parsed_req.project_name)
            self._parse_context.create_object(
                "python_requirement",
                name=parsed_req.project_name,
                requirements=[parsed_req],
                dependencies=[
                    requirements_dep,
                    *dependencies_overrides.get(normalized_proj_name, []),
                ],
                modules=normalized_module_mapping.get(normalized_proj_name),
                type_stub_modules=normalized_type_stubs_module_mapping.get(
                    normalized_proj_name),
            )