def test_requirements_txt(rule_runner: RuleRunner) -> None: """This tests that we correctly create a new python_requirement_library for each entry in a requirements.txt file. Some edge cases: * We ignore comments and options (values that start with `--`). * If a module_mapping is given, and the project is in the map, we copy over a subset of the mapping to the created target. * Projects get normalized thanks to Requirement.parse(). """ assert_python_requirements( rule_runner, "python_requirements(module_mapping={'ansicolors': ['colors']})", dedent( """\ # Comment. --find-links=https://duckduckgo.com ansicolors>=1.18.0 Django==3.2 ; python_version>'3' Un-Normalized-PROJECT # Inline comment. pip@ git+https://github.com/pypa/pip.git """ ), expected_file_dep=PythonRequirementsFile( {"sources": ["requirements.txt"]}, address=Address("", target_name="requirements.txt"), ), expected_targets=[ PythonRequirementLibrary( { "dependencies": [":requirements.txt"], "requirements": [Requirement.parse("ansicolors>=1.18.0")], "module_mapping": {"ansicolors": ["colors"]}, }, address=Address("", target_name="ansicolors"), ), PythonRequirementLibrary( { "dependencies": [":requirements.txt"], "requirements": [Requirement.parse("Django==3.2 ; python_version>'3'")], }, address=Address("", target_name="Django"), ), PythonRequirementLibrary( { "dependencies": [":requirements.txt"], "requirements": [Requirement.parse("Un_Normalized_PROJECT")], }, address=Address("", target_name="Un-Normalized-PROJECT"), ), PythonRequirementLibrary( { "dependencies": [":requirements.txt"], "requirements": [Requirement.parse("pip@ git+https://github.com/pypa/pip.git")], }, address=Address("", target_name="pip"), ), ], )
def test_pipfile_lock(rule_runner: RuleRunner) -> None: """This tests that we correctly create a new python_requirement_library for each entry in a Pipfile.lock file. Edge cases: * Develop and Default requirements are used * If a module_mapping is given, and the project is in the map, we copy over a subset of the mapping to the created target. """ assert_pipenv_requirements( rule_runner, "pipenv_requirements(module_mapping={'ansicolors': ['colors']})", { "default": { "ansicolors": { "version": ">=1.18.0" } }, "develop": { "cachetools": { "markers": "python_version ~= '3.5'", "version": "==4.1.1" } }, }, expected_file_dep=PythonRequirementsFile( {"sources": ["Pipfile.lock"]}, Address("", target_name="Pipfile.lock")), expected_targets=[ PythonRequirementLibrary( { "requirements": [Requirement.parse("ansicolors>=1.18.0")], "dependencies": [":Pipfile.lock"], "module_mapping": { "ansicolors": ["colors"] }, }, Address("", target_name="ansicolors"), ), PythonRequirementLibrary( { "requirements": [ Requirement.parse( "cachetools==4.1.1;python_version ~= '3.5'") ], "dependencies": [":Pipfile.lock"], }, Address("", target_name="cachetools"), ), ], )
def test_supply_python_requirements_file(rule_runner: RuleRunner) -> None: """This tests that we can supply our own `_python_requirements_file`.""" assert_pipenv_requirements( rule_runner, dedent( """ pipenv_requirements( requirements_relpath='custom/pipfile/Pipfile.lock', pipfile_target='//:custom_pipfile_target' ) _python_requirements_file( name='custom_pipfile_target', sources=['custom/pipfile/Pipfile.lock'] ) """ ), {"default": {"ansicolors": {"version": ">=1.18.0"}}}, expected_file_dep=PythonRequirementsFile( {"sources": ["custom/pipfile/Pipfile.lock"]}, address=Address("", target_name="custom_pipfile_target"), ), expected_targets=[ PythonRequirementLibrary( { "requirements": [Requirement.parse("ansicolors>=1.18.0")], "dependencies": ["//:custom_pipfile_target"], }, address=Address("", target_name="ansicolors"), ), ], pipfile_lock_relpath="custom/pipfile/Pipfile.lock", )
def test_relpath_override(rule_runner: RuleRunner) -> None: assert_poetry_requirements( rule_runner, "poetry_requirements(pyproject_toml_relpath='subdir/pyproject.toml')", dedent( """\ [tool.poetry.dependencies] ansicolors = ">=1.18.0" [tool.poetry.dev-dependencies] """ ), pyproject_toml_relpath="subdir/pyproject.toml", expected_file_dep=PythonRequirementsFile( {"sources": ["subdir/pyproject.toml"]}, address=Address("", target_name="subdir_pyproject.toml"), ), expected_targets=[ PythonRequirementLibrary( { "dependencies": [":subdir_pyproject.toml"], "requirements": [Requirement.parse("ansicolors>=1.18.0")], }, address=Address("", target_name="ansicolors"), ), ], )
def test_multiple_versions(rule_runner: RuleRunner) -> None: """This tests that we correctly create a new python_requirement_library for each unique dependency name in a requirements.txt file, grouping duplicated dependency names to handle multiple requirement strings per PEP 508.""" assert_python_requirements( rule_runner, "python_requirements()", dedent( """\ Django>=3.2 Django==3.2.7 confusedmonkey==86 repletewateringcan>=7 """ ), expected_file_dep=PythonRequirementsFile( {"sources": ["requirements.txt"]}, Address("", target_name="requirements.txt"), ), expected_targets=[ PythonRequirementLibrary( { "dependencies": [":requirements.txt"], "requirements": [ Requirement.parse("Django>=3.2"), Requirement.parse("Django==3.2.7"), ], }, Address("", target_name="Django"), ), PythonRequirementLibrary( { "dependencies": [":requirements.txt"], "requirements": [Requirement.parse("confusedmonkey==86")], }, Address("", target_name="confusedmonkey"), ), PythonRequirementLibrary( { "dependencies": [":requirements.txt"], "requirements": [Requirement.parse("repletewateringcan>=7")], }, Address("", target_name="repletewateringcan"), ), ], )
def test_properly_creates_extras_requirements(rule_runner: RuleRunner) -> None: """This tests the proper parsing of requirements installed with specified extras.""" assert_pipenv_requirements( rule_runner, "pipenv_requirements()", { "default": { "ansicolors": { "version": ">=1.18.0", "extras": ["neon"] } }, "develop": { "cachetools": { "markers": "python_version ~= '3.5'", "version": "==4.1.1", "extras": ["ring", "mongo"], } }, }, expected_file_dep=PythonRequirementsFile( {"sources": ["Pipfile.lock"]}, Address("", target_name="Pipfile.lock")), expected_targets=[ PythonRequirementLibrary( { "requirements": [Requirement.parse("ansicolors[neon]>=1.18.0")], "dependencies": [":Pipfile.lock"], }, Address("", target_name="ansicolors"), ), PythonRequirementLibrary( { "requirements": [ Requirement.parse( "cachetools[ring,mongo]==4.1.1;python_version ~= '3.5'" ) ], "dependencies": [":Pipfile.lock"], }, Address("", target_name="cachetools"), ), ], )
def test_relpath_override(self) -> None: self.assert_python_requirements( "python_requirements(requirements_relpath='subdir/requirements.txt')", "ansicolors>=1.18.0", requirements_txt_relpath="subdir/requirements.txt", expected_file_dep=PythonRequirementsFile( {"sources": ["subdir/requirements.txt"]}, address=Address("", target_name="subdir/requirements.txt"), ), expected_targets=[ PythonRequirementLibrary( { "dependencies": [":subdir/requirements.txt"], "requirements": [Requirement.parse("ansicolors>=1.18.0")], }, address=Address("", target_name="ansicolors"), ), ], )
def test_pyproject_toml(rule_runner: RuleRunner) -> None: """This tests that we correctly create a new python_requirement_library for each entry in a pyproject.toml file. Note that this just ensures proper targets are created; see prior tests for specific parsing edge cases. """ assert_poetry_requirements( rule_runner, dedent( """\ poetry_requirements( # module_mapping should work regardless of capitalization. module_mapping={'ansiCOLORS': ['colors']}, type_stubs_module_mapping={'Django-types': ['django']}, ) """ ), dedent( """\ [tool.poetry.dependencies] Django = {version = "3.2", python = "3"} Django-types = "2" Un-Normalized-PROJECT = "1.0.0" [tool.poetry.dev-dependencies] ansicolors = ">=1.18.0" """ ), expected_file_dep=PythonRequirementsFile( {"sources": ["pyproject.toml"]}, address=Address("", target_name="pyproject.toml"), ), expected_targets=[ PythonRequirementLibrary( { "dependencies": [":pyproject.toml"], "requirements": [Requirement.parse("ansicolors>=1.18.0")], "module_mapping": {"ansicolors": ["colors"]}, }, address=Address("", target_name="ansicolors"), ), PythonRequirementLibrary( { "dependencies": [":pyproject.toml"], "requirements": [Requirement.parse("Django==3.2 ; python_version == '3'")], }, address=Address("", target_name="Django"), ), PythonRequirementLibrary( { "dependencies": [":pyproject.toml"], "requirements": [Requirement.parse("Django-types==2")], "type_stubs_module_mapping": {"Django-types": ["django"]}, }, address=Address("", target_name="Django-types"), ), PythonRequirementLibrary( { "dependencies": [":pyproject.toml"], "requirements": [Requirement.parse("Un_Normalized_PROJECT == 1.0.0")], }, address=Address("", target_name="Un-Normalized-PROJECT"), ), ], )