1import os 2import shutil 3import subprocess 4import sys 5from textwrap import dedent 6from unittest import mock 7 8import pytest 9from pip._internal.utils.urls import path_to_url 10 11from piptools.scripts.compile import cli 12 13from .constants import MINIMAL_WHEELS_PATH, PACKAGES_PATH 14 15is_pypy = "__pypy__" in sys.builtin_module_names 16is_windows = sys.platform == "win32" 17 18 19@pytest.fixture(autouse=True) 20def _temp_dep_cache(tmpdir, monkeypatch): 21 monkeypatch.setenv("PIP_TOOLS_CACHE_DIR", str(tmpdir / "cache")) 22 23 24def test_default_pip_conf_read(pip_with_index_conf, runner): 25 # preconditions 26 with open("requirements.in", "w"): 27 pass 28 out = runner.invoke(cli, ["-v"]) 29 30 # check that we have our index-url as specified in pip.conf 31 assert "Using indexes:\n http://example.com" in out.stderr 32 assert "--index-url http://example.com" in out.stderr 33 34 35def test_command_line_overrides_pip_conf(pip_with_index_conf, runner): 36 # preconditions 37 with open("requirements.in", "w"): 38 pass 39 out = runner.invoke(cli, ["-v", "-i", "http://override.com"]) 40 41 # check that we have our index-url as specified in pip.conf 42 assert "Using indexes:\n http://override.com" in out.stderr 43 44 45@pytest.mark.network 46@pytest.mark.parametrize( 47 ("install_requires", "expected_output"), 48 ( 49 pytest.param("small-fake-a==0.1", "small-fake-a==0.1", id="regular"), 50 pytest.param( 51 "pip-tools @ https://github.com/jazzband/pip-tools/archive/7d86c8d3.zip", 52 "pip-tools @ https://github.com/jazzband/pip-tools/archive/7d86c8d3.zip", 53 id="zip URL", 54 ), 55 pytest.param( 56 "pip-tools @ git+https://github.com/jazzband/pip-tools@7d86c8d3", 57 "pip-tools @ git+https://github.com/jazzband/pip-tools@7d86c8d3", 58 id="scm URL", 59 ), 60 pytest.param( 61 "pip-tools @ https://files.pythonhosted.org/packages/06/96/" 62 "89872db07ae70770fba97205b0737c17ef013d0d1c790" 63 "899c16bb8bac419/pip_tools-3.6.1-py2.py3-none-any.whl", 64 "pip-tools @ https://files.pythonhosted.org/packages/06/96/" 65 "89872db07ae70770fba97205b0737c17ef013d0d1c790" 66 "899c16bb8bac419/pip_tools-3.6.1-py2.py3-none-any.whl", 67 id="wheel URL", 68 ), 69 ), 70) 71def test_command_line_setuptools_read( 72 runner, make_pip_conf, make_package, install_requires, expected_output 73): 74 package_dir = make_package( 75 name="fake-setuptools-a", 76 install_requires=(install_requires,), 77 ) 78 79 out = runner.invoke( 80 cli, 81 (str(package_dir / "setup.py"), "--find-links", MINIMAL_WHEELS_PATH), 82 ) 83 84 assert expected_output in out.stderr.splitlines() 85 86 # check that pip-compile generated a configuration file 87 assert (package_dir / "requirements.txt").exists() 88 89 90@pytest.mark.network 91@pytest.mark.parametrize( 92 ("options", "expected_output_file"), 93 ( 94 # For the `pip-compile` output file should be "requirements.txt" 95 ([], "requirements.txt"), 96 # For the `pip-compile --output-file=output.txt` 97 # output file should be "output.txt" 98 (["--output-file", "output.txt"], "output.txt"), 99 # For the `pip-compile setup.py` output file should be "requirements.txt" 100 (["setup.py"], "requirements.txt"), 101 # For the `pip-compile setup.py --output-file=output.txt` 102 # output file should be "output.txt" 103 (["setup.py", "--output-file", "output.txt"], "output.txt"), 104 ), 105) 106def test_command_line_setuptools_output_file(runner, options, expected_output_file): 107 """ 108 Test the output files for setup.py as a requirement file. 109 """ 110 111 with open("setup.py", "w") as package: 112 package.write( 113 dedent( 114 """\ 115 from setuptools import setup 116 setup(install_requires=[]) 117 """ 118 ) 119 ) 120 121 out = runner.invoke(cli, options) 122 assert out.exit_code == 0 123 assert os.path.exists(expected_output_file) 124 125 126@pytest.mark.network 127def test_command_line_setuptools_nested_output_file(tmpdir, runner): 128 """ 129 Test the output file for setup.py in nested folder as a requirement file. 130 """ 131 proj_dir = tmpdir.mkdir("proj") 132 133 with open(str(proj_dir / "setup.py"), "w") as package: 134 package.write( 135 dedent( 136 """\ 137 from setuptools import setup 138 setup(install_requires=[]) 139 """ 140 ) 141 ) 142 143 out = runner.invoke(cli, [str(proj_dir / "setup.py")]) 144 assert out.exit_code == 0 145 assert (proj_dir / "requirements.txt").exists() 146 147 148@pytest.mark.network 149def test_setuptools_preserves_environment_markers( 150 runner, make_package, make_wheel, make_pip_conf, tmpdir 151): 152 make_pip_conf( 153 dedent( 154 """\ 155 [global] 156 disable-pip-version-check = True 157 """ 158 ) 159 ) 160 161 dists_dir = tmpdir / "dists" 162 163 foo_dir = make_package(name="foo", version="1.0") 164 make_wheel(foo_dir, dists_dir) 165 166 bar_dir = make_package( 167 name="bar", version="2.0", install_requires=['foo ; python_version >= "1"'] 168 ) 169 out = runner.invoke( 170 cli, 171 [ 172 str(bar_dir / "setup.py"), 173 "--no-header", 174 "--no-annotate", 175 "--no-emit-find-links", 176 "--find-links", 177 str(dists_dir), 178 ], 179 ) 180 181 assert out.exit_code == 0, out.stderr 182 assert out.stderr == 'foo==1.0 ; python_version >= "1"\n' 183 184 185def test_find_links_option(runner): 186 with open("requirements.in", "w") as req_in: 187 req_in.write("-f ./libs3") 188 189 out = runner.invoke(cli, ["-v", "-f", "./libs1", "-f", "./libs2"]) 190 191 # Check that find-links has been passed to pip 192 assert "Using links:\n ./libs1\n ./libs2\n ./libs3\n" in out.stderr 193 194 # Check that find-links has been written to a requirements.txt 195 with open("requirements.txt") as req_txt: 196 assert ( 197 "--find-links ./libs1\n--find-links ./libs2\n--find-links ./libs3\n" 198 in req_txt.read() 199 ) 200 201 202def test_find_links_envvar(monkeypatch, runner): 203 with open("requirements.in", "w") as req_in: 204 req_in.write("-f ./libs3") 205 206 monkeypatch.setenv("PIP_FIND_LINKS", "./libs1 ./libs2") 207 out = runner.invoke(cli, ["-v"]) 208 209 # Check that find-links has been passed to pip 210 assert "Using links:\n ./libs1\n ./libs2\n ./libs3\n" in out.stderr 211 212 # Check that find-links has been written to a requirements.txt 213 with open("requirements.txt") as req_txt: 214 assert ( 215 "--find-links ./libs1\n--find-links ./libs2\n--find-links ./libs3\n" 216 in req_txt.read() 217 ) 218 219 220def test_extra_index_option(pip_with_index_conf, runner): 221 with open("requirements.in", "w"): 222 pass 223 out = runner.invoke( 224 cli, 225 [ 226 "-v", 227 "--extra-index-url", 228 "http://extraindex1.com", 229 "--extra-index-url", 230 "http://extraindex2.com", 231 ], 232 ) 233 assert ( 234 "Using indexes:\n" 235 " http://example.com\n" 236 " http://extraindex1.com\n" 237 " http://extraindex2.com" in out.stderr 238 ) 239 assert ( 240 "--index-url http://example.com\n" 241 "--extra-index-url http://extraindex1.com\n" 242 "--extra-index-url http://extraindex2.com" in out.stderr 243 ) 244 245 246def test_extra_index_envvar(monkeypatch, runner): 247 with open("requirements.in", "w"): 248 pass 249 250 monkeypatch.setenv("PIP_INDEX_URL", "http://example.com") 251 monkeypatch.setenv( 252 "PIP_EXTRA_INDEX_URL", "http://extraindex1.com http://extraindex2.com" 253 ) 254 out = runner.invoke(cli, ["-v"]) 255 assert ( 256 "Using indexes:\n" 257 " http://example.com\n" 258 " http://extraindex1.com\n" 259 " http://extraindex2.com" in out.stderr 260 ) 261 assert ( 262 "--index-url http://example.com\n" 263 "--extra-index-url http://extraindex1.com\n" 264 "--extra-index-url http://extraindex2.com" in out.stderr 265 ) 266 267 268@pytest.mark.parametrize("option", ("--extra-index-url", "--find-links")) 269def test_redacted_urls_in_verbose_output(runner, option): 270 """ 271 Test that URLs with sensitive data don't leak to the output. 272 """ 273 with open("requirements.in", "w"): 274 pass 275 276 out = runner.invoke( 277 cli, 278 [ 279 "--no-header", 280 "--no-emit-index-url", 281 "--no-emit-find-links", 282 "--verbose", 283 option, 284 "http://username:password@example.com", 285 ], 286 ) 287 288 assert "http://username:****@example.com" in out.stderr 289 assert "password" not in out.stderr 290 291 292def test_trusted_host_option(pip_conf, runner): 293 with open("requirements.in", "w"): 294 pass 295 out = runner.invoke( 296 cli, ["-v", "--trusted-host", "example.com", "--trusted-host", "example2.com"] 297 ) 298 assert "--trusted-host example.com\n--trusted-host example2.com\n" in out.stderr 299 300 301def test_trusted_host_envvar(monkeypatch, pip_conf, runner): 302 with open("requirements.in", "w"): 303 pass 304 monkeypatch.setenv("PIP_TRUSTED_HOST", "example.com example2.com") 305 out = runner.invoke(cli, ["-v"]) 306 assert "--trusted-host example.com\n--trusted-host example2.com\n" in out.stderr 307 308 309@pytest.mark.parametrize( 310 "options", 311 ( 312 pytest.param( 313 ["--trusted-host", "example.com", "--no-emit-trusted-host"], 314 id="trusted host", 315 ), 316 pytest.param( 317 ["--find-links", "wheels", "--no-emit-find-links"], id="find links" 318 ), 319 pytest.param( 320 ["--index-url", "https://index-url", "--no-emit-index-url"], id="index url" 321 ), 322 ), 323) 324def test_all_no_emit_options(runner, options): 325 with open("requirements.in", "w"): 326 pass 327 out = runner.invoke(cli, ["--no-header", *options]) 328 assert out.stderr.strip().splitlines() == [] 329 330 331@pytest.mark.parametrize( 332 ("option", "expected_output"), 333 ( 334 pytest.param( 335 "--emit-index-url", ["--index-url https://index-url"], id="index url" 336 ), 337 pytest.param("--no-emit-index-url", [], id="no index"), 338 ), 339) 340def test_emit_index_url_option(runner, option, expected_output): 341 with open("requirements.in", "w"): 342 pass 343 344 out = runner.invoke( 345 cli, ["--no-header", "--index-url", "https://index-url", option] 346 ) 347 348 assert out.stderr.strip().splitlines() == expected_output 349 350 351@pytest.mark.network 352@pytest.mark.xfail( 353 is_pypy and is_windows, reason="https://github.com/jazzband/pip-tools/issues/1148" 354) 355def test_realistic_complex_sub_dependencies(runner): 356 wheels_dir = "wheels" 357 358 # make a temporary wheel of a fake package 359 subprocess.run( 360 [ 361 "pip", 362 "wheel", 363 "--no-deps", 364 "-w", 365 wheels_dir, 366 os.path.join(PACKAGES_PATH, "fake_with_deps", "."), 367 ], 368 check=True, 369 ) 370 371 with open("requirements.in", "w") as req_in: 372 req_in.write("fake_with_deps") # require fake package 373 374 out = runner.invoke(cli, ["-n", "--rebuild", "-f", wheels_dir]) 375 376 assert out.exit_code == 0 377 378 379def test_run_as_module_compile(): 380 """piptools can be run as ``python -m piptools ...``.""" 381 382 result = subprocess.run( 383 [sys.executable, "-m", "piptools", "compile", "--help"], 384 stdout=subprocess.PIPE, 385 check=True, 386 ) 387 388 # Should have run pip-compile successfully. 389 assert result.stdout.startswith(b"Usage:") 390 assert b"Compiles requirements.txt from requirements.in" in result.stdout 391 392 393def test_editable_package(pip_conf, runner): 394 """piptools can compile an editable""" 395 fake_package_dir = os.path.join(PACKAGES_PATH, "small_fake_with_deps") 396 fake_package_dir = path_to_url(fake_package_dir) 397 with open("requirements.in", "w") as req_in: 398 req_in.write("-e " + fake_package_dir) # require editable fake package 399 400 out = runner.invoke(cli, ["-n"]) 401 402 assert out.exit_code == 0 403 assert fake_package_dir in out.stderr 404 assert "small-fake-a==0.1" in out.stderr 405 406 407def test_editable_package_without_non_editable_duplicate(pip_conf, runner): 408 """ 409 piptools keeps editable requirement, 410 without also adding a duplicate "non-editable" requirement variation 411 """ 412 fake_package_dir = os.path.join(PACKAGES_PATH, "small_fake_a") 413 fake_package_dir = path_to_url(fake_package_dir) 414 with open("requirements.in", "w") as req_in: 415 # small_fake_with_unpinned_deps also requires small_fake_a 416 req_in.write( 417 "-e " 418 + fake_package_dir 419 + "\nsmall_fake_with_unpinned_deps" # require editable fake package 420 ) 421 422 out = runner.invoke(cli, ["-n"]) 423 424 assert out.exit_code == 0 425 assert fake_package_dir in out.stderr 426 # Shouldn't include a non-editable small-fake-a==<version>. 427 assert "small-fake-a==" not in out.stderr 428 429 430def test_editable_package_constraint_without_non_editable_duplicate(pip_conf, runner): 431 """ 432 piptools keeps editable constraint, 433 without also adding a duplicate "non-editable" requirement variation 434 """ 435 fake_package_dir = os.path.join(PACKAGES_PATH, "small_fake_a") 436 fake_package_dir = path_to_url(fake_package_dir) 437 with open("constraints.txt", "w") as constraints: 438 constraints.write("-e " + fake_package_dir) # require editable fake package 439 440 with open("requirements.in", "w") as req_in: 441 req_in.write( 442 "-c constraints.txt" # require editable fake package 443 "\nsmall_fake_with_unpinned_deps" # This one also requires small_fake_a 444 ) 445 446 out = runner.invoke(cli, ["-n"]) 447 448 assert out.exit_code == 0 449 assert fake_package_dir in out.stderr 450 # Shouldn't include a non-editable small-fake-a==<version>. 451 assert "small-fake-a==" not in out.stderr 452 453 454@pytest.mark.parametrize("req_editable", ((True,), (False,))) 455def test_editable_package_in_constraints(pip_conf, runner, req_editable): 456 """ 457 piptools can compile an editable that appears in both primary requirements 458 and constraints 459 """ 460 fake_package_dir = os.path.join(PACKAGES_PATH, "small_fake_with_deps") 461 fake_package_dir = path_to_url(fake_package_dir) 462 463 with open("constraints.txt", "w") as constraints_in: 464 constraints_in.write("-e " + fake_package_dir) 465 466 with open("requirements.in", "w") as req_in: 467 prefix = "-e " if req_editable else "" 468 req_in.write(prefix + fake_package_dir + "\n-c constraints.txt") 469 470 out = runner.invoke(cli, ["-n"]) 471 472 assert out.exit_code == 0 473 assert fake_package_dir in out.stderr 474 assert "small-fake-a==0.1" in out.stderr 475 476 477@pytest.mark.network 478def test_editable_package_vcs(runner): 479 vcs_package = ( 480 "git+git://github.com/jazzband/pip-tools@" 481 "f97e62ecb0d9b70965c8eff952c001d8e2722e94" 482 "#egg=pip-tools" 483 ) 484 with open("requirements.in", "w") as req_in: 485 req_in.write("-e " + vcs_package) 486 out = runner.invoke(cli, ["-n", "--rebuild"]) 487 assert out.exit_code == 0 488 assert vcs_package in out.stderr 489 assert "click" in out.stderr # dependency of pip-tools 490 491 492def test_locally_available_editable_package_is_not_archived_in_cache_dir( 493 pip_conf, tmpdir, runner 494): 495 """ 496 piptools will not create an archive for a locally available editable requirement 497 """ 498 cache_dir = tmpdir.mkdir("cache_dir") 499 500 fake_package_dir = os.path.join(PACKAGES_PATH, "small_fake_with_deps") 501 fake_package_dir = path_to_url(fake_package_dir) 502 503 with open("requirements.in", "w") as req_in: 504 req_in.write("-e " + fake_package_dir) # require editable fake package 505 506 out = runner.invoke(cli, ["-n", "--rebuild", "--cache-dir", str(cache_dir)]) 507 508 assert out.exit_code == 0 509 assert fake_package_dir in out.stderr 510 assert "small-fake-a==0.1" in out.stderr 511 512 # we should not find any archived file in {cache_dir}/pkgs 513 assert not os.listdir(os.path.join(str(cache_dir), "pkgs")) 514 515 516@pytest.mark.parametrize( 517 ("line", "dependency"), 518 ( 519 # use pip-tools version prior to its use of setuptools_scm, 520 # which is incompatible with https: install 521 pytest.param( 522 "https://github.com/jazzband/pip-tools/archive/" 523 "7d86c8d3ecd1faa6be11c7ddc6b29a30ffd1dae3.zip", 524 "\nclick==", 525 id="Zip URL", 526 ), 527 pytest.param( 528 "git+git://github.com/jazzband/pip-tools@" 529 "7d86c8d3ecd1faa6be11c7ddc6b29a30ffd1dae3", 530 "\nclick==", 531 id="VCS URL", 532 ), 533 pytest.param( 534 "https://files.pythonhosted.org/packages/06/96/" 535 "89872db07ae70770fba97205b0737c17ef013d0d1c790" 536 "899c16bb8bac419/pip_tools-3.6.1-py2.py3-none-any.whl", 537 "\nclick==", 538 id="Wheel URL", 539 ), 540 pytest.param( 541 "pytest-django @ git+git://github.com/pytest-dev/pytest-django" 542 "@21492afc88a19d4ca01cd0ac392a5325b14f95c7" 543 "#egg=pytest-django", 544 "pytest-django @ git+git://github.com/pytest-dev/pytest-django" 545 "@21492afc88a19d4ca01cd0ac392a5325b14f95c7", 546 id="VCS with direct reference and egg", 547 ), 548 ), 549) 550@pytest.mark.parametrize("generate_hashes", ((True,), (False,))) 551@pytest.mark.network 552def test_url_package(runner, line, dependency, generate_hashes): 553 with open("requirements.in", "w") as req_in: 554 req_in.write(line) 555 out = runner.invoke( 556 cli, ["-n", "--rebuild"] + (["--generate-hashes"] if generate_hashes else []) 557 ) 558 assert out.exit_code == 0 559 assert dependency in out.stderr 560 561 562@pytest.mark.parametrize( 563 ("line", "dependency", "rewritten_line"), 564 ( 565 pytest.param( 566 path_to_url( 567 os.path.join( 568 MINIMAL_WHEELS_PATH, "small_fake_with_deps-0.1-py2.py3-none-any.whl" 569 ) 570 ), 571 "\nsmall-fake-a==0.1", 572 None, 573 id="Wheel URI", 574 ), 575 pytest.param( 576 path_to_url(os.path.join(PACKAGES_PATH, "small_fake_with_deps")), 577 "\nsmall-fake-a==0.1", 578 None, 579 id="Local project URI", 580 ), 581 pytest.param( 582 os.path.join( 583 MINIMAL_WHEELS_PATH, "small_fake_with_deps-0.1-py2.py3-none-any.whl" 584 ), 585 "\nsmall-fake-a==0.1", 586 path_to_url( 587 os.path.join( 588 MINIMAL_WHEELS_PATH, "small_fake_with_deps-0.1-py2.py3-none-any.whl" 589 ) 590 ), 591 id="Bare path to file URI", 592 ), 593 pytest.param( 594 os.path.join( 595 MINIMAL_WHEELS_PATH, "small_fake_with_deps-0.1-py2.py3-none-any.whl" 596 ), 597 "\nsmall-fake-with-deps @ " 598 + path_to_url( 599 os.path.join( 600 MINIMAL_WHEELS_PATH, "small_fake_with_deps-0.1-py2.py3-none-any.whl" 601 ) 602 ), 603 "\nsmall-fake-with-deps @ " 604 + path_to_url( 605 os.path.join( 606 MINIMAL_WHEELS_PATH, "small_fake_with_deps-0.1-py2.py3-none-any.whl" 607 ) 608 ), 609 id="Local project with absolute URI", 610 ), 611 pytest.param( 612 path_to_url(os.path.join(PACKAGES_PATH, "small_fake_with_subdir")) 613 + "#subdirectory=subdir&egg=small_fake_a", 614 "small-fake-a @ " 615 + path_to_url(os.path.join(PACKAGES_PATH, "small_fake_with_subdir")) 616 + "#subdirectory=subdir", 617 "small-fake-a @ " 618 + path_to_url(os.path.join(PACKAGES_PATH, "small_fake_with_subdir")) 619 + "#subdirectory=subdir", 620 id="Local project with subdirectory", 621 ), 622 ), 623) 624@pytest.mark.parametrize("generate_hashes", ((True,), (False,))) 625def test_local_file_uri_package( 626 pip_conf, runner, line, dependency, rewritten_line, generate_hashes 627): 628 if rewritten_line is None: 629 rewritten_line = line 630 with open("requirements.in", "w") as req_in: 631 req_in.write(line) 632 out = runner.invoke( 633 cli, ["-n", "--rebuild"] + (["--generate-hashes"] if generate_hashes else []) 634 ) 635 assert out.exit_code == 0 636 assert rewritten_line in out.stderr 637 assert dependency in out.stderr 638 639 640def test_relative_file_uri_package(pip_conf, runner): 641 # Copy wheel into temp dir 642 shutil.copy( 643 os.path.join( 644 MINIMAL_WHEELS_PATH, "small_fake_with_deps-0.1-py2.py3-none-any.whl" 645 ), 646 ".", 647 ) 648 with open("requirements.in", "w") as req_in: 649 req_in.write("file:small_fake_with_deps-0.1-py2.py3-none-any.whl") 650 out = runner.invoke(cli, ["-n", "--rebuild"]) 651 assert out.exit_code == 0 652 assert "file:small_fake_with_deps-0.1-py2.py3-none-any.whl" in out.stderr 653 654 655def test_direct_reference_with_extras(runner): 656 with open("requirements.in", "w") as req_in: 657 req_in.write( 658 "piptools[testing,coverage] @ git+https://github.com/jazzband/pip-tools@6.2.0" 659 ) 660 out = runner.invoke(cli, ["-n", "--rebuild"]) 661 assert out.exit_code == 0 662 assert "pip-tools @ git+https://github.com/jazzband/pip-tools@6.2.0" in out.stderr 663 assert "pytest==" in out.stderr 664 assert "pytest-cov==" in out.stderr 665 666 667def test_input_file_without_extension(pip_conf, runner): 668 """ 669 piptools can compile a file without an extension, 670 and add .txt as the defaut output file extension. 671 """ 672 with open("requirements", "w") as req_in: 673 req_in.write("small-fake-a==0.1") 674 675 out = runner.invoke(cli, ["requirements"]) 676 677 assert out.exit_code == 0 678 assert "small-fake-a==0.1" in out.stderr 679 assert os.path.exists("requirements.txt") 680 681 682def test_upgrade_packages_option(pip_conf, runner): 683 """ 684 piptools respects --upgrade-package/-P inline list. 685 """ 686 with open("requirements.in", "w") as req_in: 687 req_in.write("small-fake-a\nsmall-fake-b") 688 with open("requirements.txt", "w") as req_in: 689 req_in.write("small-fake-a==0.1\nsmall-fake-b==0.1") 690 691 out = runner.invoke(cli, ["--no-annotate", "-P", "small-fake-b"]) 692 693 assert out.exit_code == 0 694 assert "small-fake-a==0.1" in out.stderr.splitlines() 695 assert "small-fake-b==0.3" in out.stderr.splitlines() 696 697 698def test_upgrade_packages_option_irrelevant(pip_conf, runner): 699 """ 700 piptools ignores --upgrade-package/-P items not already constrained. 701 """ 702 with open("requirements.in", "w") as req_in: 703 req_in.write("small-fake-a") 704 with open("requirements.txt", "w") as req_in: 705 req_in.write("small-fake-a==0.1") 706 707 out = runner.invoke(cli, ["--no-annotate", "--upgrade-package", "small-fake-b"]) 708 709 assert out.exit_code == 0 710 assert "small-fake-a==0.1" in out.stderr.splitlines() 711 assert "small-fake-b==0.3" not in out.stderr.splitlines() 712 713 714def test_upgrade_packages_option_no_existing_file(pip_conf, runner): 715 """ 716 piptools respects --upgrade-package/-P inline list when the output file 717 doesn't exist. 718 """ 719 with open("requirements.in", "w") as req_in: 720 req_in.write("small-fake-a\nsmall-fake-b") 721 722 out = runner.invoke(cli, ["--no-annotate", "-P", "small-fake-b"]) 723 724 assert out.exit_code == 0 725 assert "small-fake-a==0.2" in out.stderr.splitlines() 726 assert "small-fake-b==0.3" in out.stderr.splitlines() 727 728 729@pytest.mark.parametrize( 730 ("current_package", "upgraded_package"), 731 ( 732 pytest.param("small-fake-b==0.1", "small-fake-b==0.3", id="upgrade"), 733 pytest.param("small-fake-b==0.3", "small-fake-b==0.1", id="downgrade"), 734 ), 735) 736def test_upgrade_packages_version_option( 737 pip_conf, runner, current_package, upgraded_package 738): 739 """ 740 piptools respects --upgrade-package/-P inline list with specified versions. 741 """ 742 with open("requirements.in", "w") as req_in: 743 req_in.write("small-fake-a\nsmall-fake-b") 744 with open("requirements.txt", "w") as req_in: 745 req_in.write("small-fake-a==0.1\n" + current_package) 746 747 out = runner.invoke(cli, ["--no-annotate", "--upgrade-package", upgraded_package]) 748 749 assert out.exit_code == 0 750 stderr_lines = out.stderr.splitlines() 751 assert "small-fake-a==0.1" in stderr_lines 752 assert upgraded_package in stderr_lines 753 754 755def test_upgrade_packages_version_option_no_existing_file(pip_conf, runner): 756 """ 757 piptools respects --upgrade-package/-P inline list with specified versions. 758 """ 759 with open("requirements.in", "w") as req_in: 760 req_in.write("small-fake-a\nsmall-fake-b") 761 762 out = runner.invoke(cli, ["-P", "small-fake-b==0.2"]) 763 764 assert out.exit_code == 0 765 assert "small-fake-a==0.2" in out.stderr 766 assert "small-fake-b==0.2" in out.stderr 767 768 769def test_upgrade_packages_version_option_and_upgrade(pip_conf, runner): 770 """ 771 piptools respects --upgrade-package/-P inline list with specified versions 772 whilst also doing --upgrade. 773 """ 774 with open("requirements.in", "w") as req_in: 775 req_in.write("small-fake-a\nsmall-fake-b") 776 with open("requirements.txt", "w") as req_in: 777 req_in.write("small-fake-a==0.1\nsmall-fake-b==0.1") 778 779 out = runner.invoke(cli, ["--upgrade", "-P", "small-fake-b==0.1"]) 780 781 assert out.exit_code == 0 782 assert "small-fake-a==0.2" in out.stderr 783 assert "small-fake-b==0.1" in out.stderr 784 785 786def test_upgrade_packages_version_option_and_upgrade_no_existing_file(pip_conf, runner): 787 """ 788 piptools respects --upgrade-package/-P inline list with specified versions 789 whilst also doing --upgrade and the output file doesn't exist. 790 """ 791 with open("requirements.in", "w") as req_in: 792 req_in.write("small-fake-a\nsmall-fake-b") 793 794 out = runner.invoke(cli, ["--upgrade", "-P", "small-fake-b==0.1"]) 795 796 assert out.exit_code == 0 797 assert "small-fake-a==0.2" in out.stderr 798 assert "small-fake-b==0.1" in out.stderr 799 800 801def test_quiet_option(runner): 802 with open("requirements", "w"): 803 pass 804 out = runner.invoke(cli, ["--quiet", "-n", "requirements"]) 805 # Pinned requirements result has not been written to output. 806 assert not out.stderr_bytes 807 808 809def test_dry_run_noisy_option(runner): 810 with open("requirements", "w"): 811 pass 812 out = runner.invoke(cli, ["--dry-run", "requirements"]) 813 # Dry-run message has been written to output 814 assert "Dry-run, so nothing updated." in out.stderr.splitlines() 815 816 817def test_dry_run_quiet_option(runner): 818 with open("requirements", "w"): 819 pass 820 out = runner.invoke(cli, ["--dry-run", "--quiet", "requirements"]) 821 # Dry-run message has not been written to output. 822 assert not out.stderr_bytes 823 824 825def test_generate_hashes_with_editable(pip_conf, runner): 826 small_fake_package_dir = os.path.join(PACKAGES_PATH, "small_fake_with_deps") 827 small_fake_package_url = path_to_url(small_fake_package_dir) 828 with open("requirements.in", "w") as fp: 829 fp.write(f"-e {small_fake_package_url}\n") 830 out = runner.invoke(cli, ["--no-annotate", "--generate-hashes"]) 831 expected = ( 832 "-e {}\n" 833 "small-fake-a==0.1 \\\n" 834 " --hash=sha256:5e6071ee6e4c59e0d0408d366f" 835 "e9b66781d2cf01be9a6e19a2433bb3c5336330\n" 836 "small-fake-b==0.1 \\\n" 837 " --hash=sha256:acdba8f8b8a816213c30d5310c" 838 "3fe296c0107b16ed452062f7f994a5672e3b3f\n" 839 ).format(small_fake_package_url) 840 assert out.exit_code == 0 841 assert expected in out.stderr 842 843 844@pytest.mark.network 845def test_generate_hashes_with_url(runner): 846 with open("requirements.in", "w") as fp: 847 fp.write( 848 "https://github.com/jazzband/pip-tools/archive/" 849 "7d86c8d3ecd1faa6be11c7ddc6b29a30ffd1dae3.zip#egg=pip-tools\n" 850 ) 851 out = runner.invoke(cli, ["--no-annotate", "--generate-hashes"]) 852 expected = ( 853 "pip-tools @ https://github.com/jazzband/pip-tools/archive/" 854 "7d86c8d3ecd1faa6be11c7ddc6b29a30ffd1dae3.zip \\\n" 855 " --hash=sha256:d24de92e18ad5bf291f25cfcdcf" 856 "0171be6fa70d01d0bef9eeda356b8549715e7\n" 857 ) 858 assert out.exit_code == 0 859 assert expected in out.stderr 860 861 862def test_generate_hashes_verbose(pip_conf, runner): 863 """ 864 The hashes generation process should show a progress. 865 """ 866 with open("requirements.in", "w") as fp: 867 fp.write("small-fake-a==0.1") 868 869 out = runner.invoke(cli, ["--generate-hashes", "-v"]) 870 expected_verbose_text = "Generating hashes:\n small-fake-a\n" 871 assert expected_verbose_text in out.stderr 872 873 874@pytest.mark.network 875def test_generate_hashes_with_annotations(runner): 876 with open("requirements.in", "w") as fp: 877 fp.write("six==1.15.0") 878 879 out = runner.invoke(cli, ["--generate-hashes"]) 880 assert out.stderr == dedent( 881 f"""\ 882 # 883 # This file is autogenerated by pip-compile with python \ 884{sys.version_info.major}.{sys.version_info.minor} 885 # To update, run: 886 # 887 # pip-compile --generate-hashes 888 # 889 six==1.15.0 \\ 890 --hash=sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259 \\ 891 --hash=sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced 892 # via -r requirements.in 893 """ 894 ) 895 896 897@pytest.mark.network 898def test_generate_hashes_with_split_style_annotations(runner): 899 with open("requirements.in", "w") as fp: 900 fp.write("Django==1.11.29\n") 901 fp.write("django-debug-toolbar==1.11\n") 902 fp.write("django-storages==1.9.1\n") 903 fp.write("django-taggit==0.24.0\n") 904 fp.write("pytz==2020.4\n") 905 fp.write("sqlparse==0.3.1\n") 906 907 out = runner.invoke(cli, ["--generate-hashes", "--annotation-style", "split"]) 908 assert out.stderr == dedent( 909 f"""\ 910 # 911 # This file is autogenerated by pip-compile with python \ 912{sys.version_info.major}.{sys.version_info.minor} 913 # To update, run: 914 # 915 # pip-compile --generate-hashes 916 # 917 django==1.11.29 \\ 918 --hash=sha256:014e3392058d94f40569206a24523ce254d55ad2f9f46c6550b0fe2e4f94cf3f \\ 919 --hash=sha256:4200aefb6678019a0acf0005cd14cfce3a5e6b9b90d06145fcdd2e474ad4329c 920 # via 921 # -r requirements.in 922 # django-debug-toolbar 923 # django-storages 924 # django-taggit 925 django-debug-toolbar==1.11 \\ 926 --hash=sha256:89d75b60c65db363fb24688d977e5fbf0e73386c67acf562d278402a10fc3736 \\ 927 --hash=sha256:c2b0134119a624f4ac9398b44f8e28a01c7686ac350a12a74793f3dd57a9eea0 928 # via -r requirements.in 929 django-storages==1.9.1 \\ 930 --hash=sha256:3103991c2ee8cef8a2ff096709973ffe7106183d211a79f22cf855f33533d924 \\ 931 --hash=sha256:a59e9923cbce7068792f75344ed7727021ee4ac20f227cf17297d0d03d141e91 932 # via -r requirements.in 933 django-taggit==0.24.0 \\ 934 --hash=sha256:710b4d15ec1996550cc68a0abbc41903ca7d832540e52b1336e6858737e410d8 \\ 935 --hash=sha256:bb8f27684814cd1414b2af75b857b5e26a40912631904038a7ecacd2bfafc3ac 936 # via -r requirements.in 937 pytz==2020.4 \\ 938 --hash=sha256:3e6b7dd2d1e0a59084bcee14a17af60c5c562cdc16d828e8eba2e683d3a7e268 \\ 939 --hash=sha256:5c55e189b682d420be27c6995ba6edce0c0a77dd67bfbe2ae6607134d5851ffd 940 # via 941 # -r requirements.in 942 # django 943 sqlparse==0.3.1 \\ 944 --hash=sha256:022fb9c87b524d1f7862b3037e541f68597a730a8843245c349fc93e1643dc4e \\ 945 --hash=sha256:e162203737712307dfe78860cc56c8da8a852ab2ee33750e33aeadf38d12c548 946 # via 947 # -r requirements.in 948 # django-debug-toolbar 949 """ 950 ) 951 952 953@pytest.mark.network 954def test_generate_hashes_with_line_style_annotations(runner): 955 with open("requirements.in", "w") as fp: 956 fp.write("Django==1.11.29\n") 957 fp.write("django-debug-toolbar==1.11\n") 958 fp.write("django-storages==1.9.1\n") 959 fp.write("django-taggit==0.24.0\n") 960 fp.write("pytz==2020.4\n") 961 fp.write("sqlparse==0.3.1\n") 962 963 out = runner.invoke(cli, ["--generate-hashes", "--annotation-style", "line"]) 964 assert out.stderr == dedent( 965 f"""\ 966 # 967 # This file is autogenerated by pip-compile with python \ 968{sys.version_info.major}.{sys.version_info.minor} 969 # To update, run: 970 # 971 # pip-compile --annotation-style=line --generate-hashes 972 # 973 django==1.11.29 \\ 974 --hash=sha256:014e3392058d94f40569206a24523ce254d55ad2f9f46c6550b0fe2e4f94cf3f \\ 975 --hash=sha256:4200aefb6678019a0acf0005cd14cfce3a5e6b9b90d06145fcdd2e474ad4329c 976 # via -r requirements.in, django-debug-toolbar, django-storages, django-taggit 977 django-debug-toolbar==1.11 \\ 978 --hash=sha256:89d75b60c65db363fb24688d977e5fbf0e73386c67acf562d278402a10fc3736 \\ 979 --hash=sha256:c2b0134119a624f4ac9398b44f8e28a01c7686ac350a12a74793f3dd57a9eea0 980 # via -r requirements.in 981 django-storages==1.9.1 \\ 982 --hash=sha256:3103991c2ee8cef8a2ff096709973ffe7106183d211a79f22cf855f33533d924 \\ 983 --hash=sha256:a59e9923cbce7068792f75344ed7727021ee4ac20f227cf17297d0d03d141e91 984 # via -r requirements.in 985 django-taggit==0.24.0 \\ 986 --hash=sha256:710b4d15ec1996550cc68a0abbc41903ca7d832540e52b1336e6858737e410d8 \\ 987 --hash=sha256:bb8f27684814cd1414b2af75b857b5e26a40912631904038a7ecacd2bfafc3ac 988 # via -r requirements.in 989 pytz==2020.4 \\ 990 --hash=sha256:3e6b7dd2d1e0a59084bcee14a17af60c5c562cdc16d828e8eba2e683d3a7e268 \\ 991 --hash=sha256:5c55e189b682d420be27c6995ba6edce0c0a77dd67bfbe2ae6607134d5851ffd 992 # via -r requirements.in, django 993 sqlparse==0.3.1 \\ 994 --hash=sha256:022fb9c87b524d1f7862b3037e541f68597a730a8843245c349fc93e1643dc4e \\ 995 --hash=sha256:e162203737712307dfe78860cc56c8da8a852ab2ee33750e33aeadf38d12c548 996 # via -r requirements.in, django-debug-toolbar 997 """ 998 ) 999 1000 1001def test_filter_pip_markers(pip_conf, runner): 1002 """ 1003 Check that pip-compile works with pip environment markers (PEP496) 1004 """ 1005 with open("requirements", "w") as req_in: 1006 req_in.write("small-fake-a==0.1\nunknown_package==0.1; python_version == '1'") 1007 1008 out = runner.invoke(cli, ["-n", "requirements"]) 1009 1010 assert out.exit_code == 0 1011 assert "small-fake-a==0.1" in out.stderr 1012 assert "unknown_package" not in out.stderr 1013 1014 1015def test_no_candidates(pip_conf, runner): 1016 with open("requirements", "w") as req_in: 1017 req_in.write("small-fake-a>0.3b1,<0.3b2") 1018 1019 out = runner.invoke(cli, ["-n", "requirements"]) 1020 1021 assert out.exit_code == 2 1022 assert "Skipped pre-versions:" in out.stderr 1023 1024 1025def test_no_candidates_pre(pip_conf, runner): 1026 with open("requirements", "w") as req_in: 1027 req_in.write("small-fake-a>0.3b1,<0.3b1") 1028 1029 out = runner.invoke(cli, ["-n", "requirements", "--pre"]) 1030 1031 assert out.exit_code == 2 1032 assert "Tried pre-versions:" in out.stderr 1033 1034 1035@pytest.mark.parametrize( 1036 ("url", "expected_url"), 1037 ( 1038 pytest.param("https://example.com", b"https://example.com", id="regular url"), 1039 pytest.param( 1040 "https://username:password@example.com", 1041 b"https://username:****@example.com", 1042 id="url with credentials", 1043 ), 1044 ), 1045) 1046def test_default_index_url(make_pip_conf, url, expected_url): 1047 """ 1048 Test help's output with default index URL. 1049 """ 1050 make_pip_conf( 1051 dedent( 1052 f"""\ 1053 [global] 1054 index-url = {url} 1055 """ 1056 ) 1057 ) 1058 1059 result = subprocess.run( 1060 [sys.executable, "-m", "piptools", "compile", "--help"], 1061 stdout=subprocess.PIPE, 1062 check=True, 1063 ) 1064 1065 assert expected_url in result.stdout 1066 1067 1068def test_stdin_without_output_file(runner): 1069 """ 1070 The --output-file option is required for STDIN. 1071 """ 1072 out = runner.invoke(cli, ["-n", "-"]) 1073 1074 assert out.exit_code == 2 1075 assert "--output-file is required if input is from stdin" in out.stderr 1076 1077 1078def test_not_specified_input_file(runner): 1079 """ 1080 It should raise an error if there are no input files or default input files 1081 such as "setup.py" or "requirements.in". 1082 """ 1083 out = runner.invoke(cli) 1084 assert "If you do not specify an input file" in out.stderr 1085 assert out.exit_code == 2 1086 1087 1088def test_stdin(pip_conf, runner): 1089 """ 1090 Test compile requirements from STDIN. 1091 """ 1092 out = runner.invoke( 1093 cli, 1094 ["-", "--output-file", "requirements.txt", "-n", "--no-emit-find-links"], 1095 input="small-fake-a==0.1", 1096 ) 1097 1098 assert out.stderr == dedent( 1099 f"""\ 1100 # 1101 # This file is autogenerated by pip-compile with python \ 1102{sys.version_info.major}.{sys.version_info.minor} 1103 # To update, run: 1104 # 1105 # pip-compile --no-emit-find-links --output-file=requirements.txt - 1106 # 1107 small-fake-a==0.1 1108 # via -r - 1109 Dry-run, so nothing updated. 1110 """ 1111 ) 1112 1113 1114def test_multiple_input_files_without_output_file(runner): 1115 """ 1116 The --output-file option is required for multiple requirement input files. 1117 """ 1118 with open("src_file1.in", "w") as req_in: 1119 req_in.write("six==1.10.0") 1120 1121 with open("src_file2.in", "w") as req_in: 1122 req_in.write("django==2.1") 1123 1124 out = runner.invoke(cli, ["src_file1.in", "src_file2.in"]) 1125 1126 assert ( 1127 "--output-file is required if two or more input files are given" in out.stderr 1128 ) 1129 assert out.exit_code == 2 1130 1131 1132@pytest.mark.parametrize( 1133 ("options", "expected"), 1134 ( 1135 pytest.param( 1136 ("--annotate",), 1137 f"""\ 1138 # 1139 # This file is autogenerated by pip-compile with python \ 1140{sys.version_info.major}.{sys.version_info.minor} 1141 # To update, run: 1142 # 1143 # pip-compile --no-emit-find-links 1144 # 1145 small-fake-a==0.1 1146 # via 1147 # -c constraints.txt 1148 # small-fake-with-deps 1149 small-fake-with-deps==0.1 1150 # via -r requirements.in 1151 Dry-run, so nothing updated. 1152 """, 1153 id="annotate", 1154 ), 1155 pytest.param( 1156 ("--annotate", "--annotation-style", "line"), 1157 f"""\ 1158 # 1159 # This file is autogenerated by pip-compile with python \ 1160{sys.version_info.major}.{sys.version_info.minor} 1161 # To update, run: 1162 # 1163 # pip-compile --annotation-style=line --no-emit-find-links 1164 # 1165 small-fake-a==0.1 # via -c constraints.txt, small-fake-with-deps 1166 small-fake-with-deps==0.1 # via -r requirements.in 1167 Dry-run, so nothing updated. 1168 """, 1169 id="annotate line style", 1170 ), 1171 pytest.param( 1172 ("--no-annotate",), 1173 f"""\ 1174 # 1175 # This file is autogenerated by pip-compile with python \ 1176{sys.version_info.major}.{sys.version_info.minor} 1177 # To update, run: 1178 # 1179 # pip-compile --no-annotate --no-emit-find-links 1180 # 1181 small-fake-a==0.1 1182 small-fake-with-deps==0.1 1183 Dry-run, so nothing updated. 1184 """, 1185 id="no annotate", 1186 ), 1187 ), 1188) 1189def test_annotate_option(pip_conf, runner, options, expected): 1190 """ 1191 The output lines have annotations if the option is turned on. 1192 """ 1193 with open("constraints.txt", "w") as constraints_in: 1194 constraints_in.write("small-fake-a==0.1") 1195 with open("requirements.in", "w") as req_in: 1196 req_in.write("-c constraints.txt\n") 1197 req_in.write("small_fake_with_deps") 1198 1199 out = runner.invoke(cli, [*options, "-n", "--no-emit-find-links"]) 1200 1201 assert out.stderr == dedent(expected) 1202 assert out.exit_code == 0 1203 1204 1205@pytest.mark.parametrize( 1206 ("option", "expected"), 1207 ( 1208 ("--allow-unsafe", "small-fake-a==0.1"), 1209 ("--no-allow-unsafe", "# small-fake-a"), 1210 (None, "# small-fake-a"), 1211 ), 1212) 1213def test_allow_unsafe_option(pip_conf, monkeypatch, runner, option, expected): 1214 """ 1215 Unsafe packages are printed as expected with and without --allow-unsafe. 1216 """ 1217 monkeypatch.setattr("piptools.resolver.UNSAFE_PACKAGES", {"small-fake-a"}) 1218 with open("requirements.in", "w") as req_in: 1219 req_in.write(path_to_url(os.path.join(PACKAGES_PATH, "small_fake_with_deps"))) 1220 1221 out = runner.invoke(cli, ["--no-annotate", option] if option else []) 1222 1223 assert expected in out.stderr.splitlines() 1224 assert out.exit_code == 0 1225 1226 1227@pytest.mark.parametrize( 1228 ("option", "attr", "expected"), 1229 (("--cert", "cert", "foo.crt"), ("--client-cert", "client_cert", "bar.pem")), 1230) 1231@mock.patch("piptools.scripts.compile.parse_requirements") 1232def test_cert_option(parse_requirements, runner, option, attr, expected): 1233 """ 1234 The options --cert and --client-cert have to be passed to the PyPIRepository. 1235 """ 1236 with open("requirements.in", "w"): 1237 pass 1238 1239 runner.invoke(cli, [option, expected]) 1240 1241 # Ensure the options in parse_requirements has the expected option 1242 args, kwargs = parse_requirements.call_args 1243 assert getattr(kwargs["options"], attr) == expected 1244 1245 1246@pytest.mark.parametrize( 1247 ("option", "expected"), 1248 (("--build-isolation", True), ("--no-build-isolation", False)), 1249) 1250@mock.patch("piptools.scripts.compile.parse_requirements") 1251def test_build_isolation_option(parse_requirements, runner, option, expected): 1252 """ 1253 A value of the --build-isolation/--no-build-isolation flag 1254 must be passed to parse_requirements(). 1255 """ 1256 with open("requirements.in", "w"): 1257 pass 1258 1259 runner.invoke(cli, [option]) 1260 1261 # Ensure the options in parse_requirements has the expected build_isolation option 1262 args, kwargs = parse_requirements.call_args 1263 assert kwargs["options"].build_isolation is expected 1264 1265 1266@mock.patch("piptools.scripts.compile.PyPIRepository") 1267def test_forwarded_args(PyPIRepository, runner): 1268 """ 1269 Test the forwarded cli args (--pip-args 'arg...') are passed to the pip command. 1270 """ 1271 with open("requirements.in", "w"): 1272 pass 1273 1274 cli_args = ("--no-annotate", "--generate-hashes") 1275 pip_args = ("--no-color", "--isolated", "--disable-pip-version-check") 1276 runner.invoke(cli, [*cli_args, "--pip-args", " ".join(pip_args)]) 1277 args, kwargs = PyPIRepository.call_args 1278 assert set(pip_args).issubset(set(args[0])) 1279 1280 1281@pytest.mark.parametrize( 1282 ("cli_option", "infile_option", "expected_package"), 1283 ( 1284 # no --pre pip-compile should resolve to the last stable version 1285 (False, False, "small-fake-a==0.2"), 1286 # pip-compile --pre should resolve to the last pre-released version 1287 (True, False, "small-fake-a==0.3b1"), 1288 (False, True, "small-fake-a==0.3b1"), 1289 (True, True, "small-fake-a==0.3b1"), 1290 ), 1291) 1292def test_pre_option(pip_conf, runner, cli_option, infile_option, expected_package): 1293 """ 1294 Tests pip-compile respects --pre option. 1295 """ 1296 with open("requirements.in", "w") as req_in: 1297 if infile_option: 1298 req_in.write("--pre\n") 1299 req_in.write("small-fake-a\n") 1300 1301 out = runner.invoke(cli, ["--no-annotate", "-n"] + (["-p"] if cli_option else [])) 1302 1303 assert out.exit_code == 0, out.stderr 1304 assert expected_package in out.stderr.splitlines(), out.stderr 1305 1306 1307@pytest.mark.parametrize( 1308 "add_options", 1309 ( 1310 [], 1311 ["--output-file", "requirements.txt"], 1312 ["--upgrade"], 1313 ["--upgrade", "--output-file", "requirements.txt"], 1314 ["--upgrade-package", "small-fake-a"], 1315 ["--upgrade-package", "small-fake-a", "--output-file", "requirements.txt"], 1316 ), 1317) 1318def test_dry_run_option(pip_conf, runner, add_options): 1319 """ 1320 Tests pip-compile doesn't create requirements.txt file on dry-run. 1321 """ 1322 with open("requirements.in", "w") as req_in: 1323 req_in.write("small-fake-a\n") 1324 1325 out = runner.invoke(cli, ["--no-annotate", "--dry-run", *add_options]) 1326 1327 assert out.exit_code == 0, out.stderr 1328 assert "small-fake-a==0.2" in out.stderr.splitlines() 1329 assert not os.path.exists("requirements.txt") 1330 1331 1332@pytest.mark.parametrize( 1333 ("add_options", "expected_cli_output_package"), 1334 ( 1335 ([], "small-fake-a==0.1"), 1336 (["--output-file", "requirements.txt"], "small-fake-a==0.1"), 1337 (["--upgrade"], "small-fake-a==0.2"), 1338 (["--upgrade", "--output-file", "requirements.txt"], "small-fake-a==0.2"), 1339 (["--upgrade-package", "small-fake-a"], "small-fake-a==0.2"), 1340 ( 1341 ["--upgrade-package", "small-fake-a", "--output-file", "requirements.txt"], 1342 "small-fake-a==0.2", 1343 ), 1344 ), 1345) 1346def test_dry_run_doesnt_touch_output_file( 1347 pip_conf, runner, add_options, expected_cli_output_package 1348): 1349 """ 1350 Tests pip-compile doesn't touch requirements.txt file on dry-run. 1351 """ 1352 with open("requirements.in", "w") as req_in: 1353 req_in.write("small-fake-a\n") 1354 1355 with open("requirements.txt", "w") as req_txt: 1356 req_txt.write("small-fake-a==0.1\n") 1357 1358 before_compile_mtime = os.stat("requirements.txt").st_mtime 1359 1360 out = runner.invoke(cli, ["--no-annotate", "--dry-run", *add_options]) 1361 1362 assert out.exit_code == 0, out.stderr 1363 assert expected_cli_output_package in out.stderr.splitlines() 1364 1365 # The package version must NOT be updated in the output file 1366 with open("requirements.txt") as req_txt: 1367 assert "small-fake-a==0.1" in req_txt.read().splitlines() 1368 1369 # The output file must not be touched 1370 after_compile_mtime = os.stat("requirements.txt").st_mtime 1371 assert after_compile_mtime == before_compile_mtime 1372 1373 1374@pytest.mark.parametrize( 1375 ("empty_input_pkg", "prior_output_pkg"), 1376 ( 1377 ("", ""), 1378 ("", "small-fake-a==0.1\n"), 1379 ("# Nothing to see here", ""), 1380 ("# Nothing to see here", "small-fake-a==0.1\n"), 1381 ), 1382) 1383def test_empty_input_file_no_header(runner, empty_input_pkg, prior_output_pkg): 1384 """ 1385 Tests pip-compile creates an empty requirements.txt file, 1386 given --no-header and empty requirements.in 1387 """ 1388 with open("requirements.in", "w") as req_in: 1389 req_in.write(empty_input_pkg) # empty input file 1390 1391 with open("requirements.txt", "w") as req_txt: 1392 req_txt.write(prior_output_pkg) 1393 1394 runner.invoke(cli, ["--no-header", "requirements.in"]) 1395 1396 with open("requirements.txt") as req_txt: 1397 assert req_txt.read().strip() == "" 1398 1399 1400def test_upgrade_package_doesnt_remove_annotation(pip_conf, runner): 1401 """ 1402 Tests pip-compile --upgrade-package shouldn't remove "via" annotation. 1403 See: GH-929 1404 """ 1405 with open("requirements.in", "w") as req_in: 1406 req_in.write("small-fake-with-deps\n") 1407 1408 runner.invoke(cli) 1409 1410 # Downgrade small-fake-a to 0.1 1411 with open("requirements.txt", "w") as req_txt: 1412 req_txt.write( 1413 "small-fake-with-deps==0.1\n" 1414 "small-fake-a==0.1 # via small-fake-with-deps\n" 1415 ) 1416 1417 runner.invoke(cli, ["-P", "small-fake-a", "--no-emit-find-links"]) 1418 with open("requirements.txt") as req_txt: 1419 assert req_txt.read() == dedent( 1420 f"""\ 1421 # 1422 # This file is autogenerated by pip-compile with python \ 1423{sys.version_info.major}.{sys.version_info.minor} 1424 # To update, run: 1425 # 1426 # pip-compile --no-emit-find-links 1427 # 1428 small-fake-a==0.1 1429 # via small-fake-with-deps 1430 small-fake-with-deps==0.1 1431 # via -r requirements.in 1432 """ 1433 ) 1434 1435 1436@pytest.mark.parametrize( 1437 "options", 1438 ( 1439 "--index-url https://example.com", 1440 "--extra-index-url https://example.com", 1441 "--find-links ./libs1", 1442 "--trusted-host example.com", 1443 "--no-binary :all:", 1444 "--only-binary :all:", 1445 ), 1446) 1447def test_options_in_requirements_file(runner, options): 1448 """ 1449 Test the options from requirements.in is copied to requirements.txt. 1450 """ 1451 with open("requirements.in", "w") as reqs_in: 1452 reqs_in.write(options) 1453 1454 out = runner.invoke(cli) 1455 assert out.exit_code == 0, out 1456 1457 with open("requirements.txt") as reqs_txt: 1458 assert options in reqs_txt.read().splitlines() 1459 1460 1461@pytest.mark.parametrize( 1462 ("cli_options", "expected_message"), 1463 ( 1464 pytest.param( 1465 ["--index-url", "scheme://foo"], 1466 "Was scheme://foo reachable?", 1467 id="single index url", 1468 ), 1469 pytest.param( 1470 ["--index-url", "scheme://foo", "--extra-index-url", "scheme://bar"], 1471 "Were scheme://foo or scheme://bar reachable?", 1472 id="multiple index urls", 1473 ), 1474 pytest.param( 1475 ["--index-url", "scheme://username:password@host"], 1476 "Was scheme://username:****@host reachable?", 1477 id="index url with credentials", 1478 ), 1479 ), 1480) 1481def test_unreachable_index_urls(runner, cli_options, expected_message): 1482 """ 1483 Test pip-compile raises an error if index URLs are not reachable. 1484 """ 1485 with open("requirements.in", "w") as reqs_in: 1486 reqs_in.write("some-package") 1487 1488 out = runner.invoke(cli, cli_options) 1489 1490 assert out.exit_code == 2, out 1491 1492 stderr_lines = out.stderr.splitlines() 1493 assert "No versions found" in stderr_lines 1494 assert expected_message in stderr_lines 1495 1496 1497@pytest.mark.parametrize( 1498 ("current_package", "upgraded_package"), 1499 ( 1500 pytest.param("small-fake-b==0.1", "small-fake-b==0.2", id="upgrade"), 1501 pytest.param("small-fake-b==0.2", "small-fake-b==0.1", id="downgrade"), 1502 ), 1503) 1504def test_upgrade_packages_option_subdependency( 1505 pip_conf, runner, current_package, upgraded_package 1506): 1507 """ 1508 Test that pip-compile --upgrade-package/-P upgrades/dpwngrades subdependencies. 1509 """ 1510 1511 with open("requirements.in", "w") as reqs: 1512 reqs.write("small-fake-with-unpinned-deps\n") 1513 1514 with open("requirements.txt", "w") as reqs: 1515 reqs.write("small-fake-a==0.1\n") 1516 reqs.write(current_package + "\n") 1517 reqs.write("small-fake-with-unpinned-deps==0.1\n") 1518 1519 out = runner.invoke( 1520 cli, ["--no-annotate", "--dry-run", "--upgrade-package", upgraded_package] 1521 ) 1522 1523 stderr_lines = out.stderr.splitlines() 1524 assert "small-fake-a==0.1" in stderr_lines, "small-fake-a must keep its version" 1525 assert ( 1526 upgraded_package in stderr_lines 1527 ), f"{current_package} must be upgraded/downgraded to {upgraded_package}" 1528 1529 1530@pytest.mark.parametrize( 1531 ("input_opts", "output_opts"), 1532 ( 1533 # Test that input options overwrite output options 1534 pytest.param( 1535 "--index-url https://index-url", 1536 "--index-url https://another-index-url", 1537 id="index url", 1538 ), 1539 pytest.param( 1540 "--extra-index-url https://extra-index-url", 1541 "--extra-index-url https://another-extra-index-url", 1542 id="extra index url", 1543 ), 1544 pytest.param("--find-links dir", "--find-links another-dir", id="find links"), 1545 pytest.param( 1546 "--trusted-host hostname", 1547 "--trusted-host another-hostname", 1548 id="trusted host", 1549 ), 1550 pytest.param( 1551 "--no-binary :package:", "--no-binary :another-package:", id="no binary" 1552 ), 1553 pytest.param( 1554 "--only-binary :package:", 1555 "--only-binary :another-package:", 1556 id="only binary", 1557 ), 1558 # Test misc corner cases 1559 pytest.param("", "--index-url https://index-url", id="empty input options"), 1560 pytest.param( 1561 "--index-url https://index-url", 1562 ( 1563 "--index-url https://index-url\n" 1564 "--extra-index-url https://another-extra-index-url" 1565 ), 1566 id="partially matched options", 1567 ), 1568 ), 1569) 1570def test_remove_outdated_options(runner, input_opts, output_opts): 1571 """ 1572 Test that the options from the current requirements.txt wouldn't stay 1573 after compile if they were removed from requirements.in file. 1574 """ 1575 with open("requirements.in", "w") as req_in: 1576 req_in.write(input_opts) 1577 with open("requirements.txt", "w") as req_txt: 1578 req_txt.write(output_opts) 1579 1580 out = runner.invoke(cli, ["--no-header"]) 1581 1582 assert out.exit_code == 0, out 1583 assert out.stderr.strip() == input_opts 1584 1585 1586def test_sub_dependencies_with_constraints(pip_conf, runner): 1587 # Write constraints file 1588 with open("constraints.txt", "w") as constraints_in: 1589 constraints_in.write("small-fake-a==0.1\n") 1590 constraints_in.write("small-fake-b==0.2\n") 1591 constraints_in.write("small-fake-with-unpinned-deps==0.1") 1592 1593 with open("requirements.in", "w") as req_in: 1594 req_in.write("-c constraints.txt\n") 1595 req_in.write("small_fake_with_deps_and_sub_deps") # require fake package 1596 1597 out = runner.invoke(cli, ["--no-annotate"]) 1598 1599 assert out.exit_code == 0 1600 1601 req_out_lines = set(out.stderr.splitlines()) 1602 assert { 1603 "small-fake-a==0.1", 1604 "small-fake-b==0.2", 1605 "small-fake-with-deps-and-sub-deps==0.1", 1606 "small-fake-with-unpinned-deps==0.1", 1607 }.issubset(req_out_lines) 1608 1609 1610def test_preserve_compiled_prerelease_version(pip_conf, runner): 1611 with open("requirements.in", "w") as req_in: 1612 req_in.write("small-fake-a") 1613 1614 with open("requirements.txt", "w") as req_txt: 1615 req_txt.write("small-fake-a==0.3b1") 1616 1617 out = runner.invoke(cli, ["--no-annotate", "--no-header"]) 1618 1619 assert out.exit_code == 0, out 1620 assert "small-fake-a==0.3b1" in out.stderr.splitlines() 1621 1622 1623def test_prefer_binary_dist( 1624 pip_conf, make_package, make_sdist, make_wheel, tmpdir, runner 1625): 1626 """ 1627 Test pip-compile chooses a correct version of a package with 1628 a binary distribution when PIP_PREFER_BINARY environment variable is on. 1629 """ 1630 dists_dir = tmpdir / "dists" 1631 1632 # Make first-package==1.0 and wheels 1633 first_package_v1 = make_package(name="first-package", version="1.0") 1634 make_wheel(first_package_v1, dists_dir) 1635 1636 # Make first-package==2.0 and sdists 1637 first_package_v2 = make_package(name="first-package", version="2.0") 1638 make_sdist(first_package_v2, dists_dir) 1639 1640 # Make second-package==1.0 which depends on first-package, and wheels 1641 second_package_v1 = make_package( 1642 name="second-package", version="1.0", install_requires=["first-package"] 1643 ) 1644 make_wheel(second_package_v1, dists_dir) 1645 1646 with open("requirements.in", "w") as req_in: 1647 req_in.write("second-package") 1648 1649 out = runner.invoke( 1650 cli, 1651 ["--no-annotate", "--find-links", str(dists_dir)], 1652 env={"PIP_PREFER_BINARY": "1"}, 1653 ) 1654 1655 assert out.exit_code == 0, out 1656 assert "first-package==1.0" in out.stderr.splitlines(), out.stderr 1657 assert "second-package==1.0" in out.stderr.splitlines(), out.stderr 1658 1659 1660@pytest.mark.parametrize("prefer_binary", (True, False)) 1661def test_prefer_binary_dist_even_there_is_source_dists( 1662 pip_conf, make_package, make_sdist, make_wheel, tmpdir, runner, prefer_binary 1663): 1664 """ 1665 Test pip-compile chooses a correct version of a package with a binary distribution 1666 (despite a source dist existing) when PIP_PREFER_BINARY environment variable is on 1667 or off. 1668 1669 Regression test for issue GH-1118. 1670 """ 1671 dists_dir = tmpdir / "dists" 1672 1673 # Make first version of package with only wheels 1674 package_v1 = make_package(name="test-package", version="1.0") 1675 make_wheel(package_v1, dists_dir) 1676 1677 # Make seconds version with wheels and sdists 1678 package_v2 = make_package(name="test-package", version="2.0") 1679 make_wheel(package_v2, dists_dir) 1680 make_sdist(package_v2, dists_dir) 1681 1682 with open("requirements.in", "w") as req_in: 1683 req_in.write("test-package") 1684 1685 out = runner.invoke( 1686 cli, 1687 ["--no-annotate", "--find-links", str(dists_dir)], 1688 env={"PIP_PREFER_BINARY": str(int(prefer_binary))}, 1689 ) 1690 1691 assert out.exit_code == 0, out 1692 assert "test-package==2.0" in out.stderr.splitlines(), out.stderr 1693 1694 1695@pytest.mark.parametrize("output_content", ("test-package-1==0.1", "")) 1696def test_duplicate_reqs_combined( 1697 pip_conf, make_package, make_sdist, tmpdir, runner, output_content 1698): 1699 """ 1700 Test pip-compile tracks dependencies properly when install requirements are 1701 combined, especially when an output file already exists. 1702 1703 Regression test for issue GH-1154. 1704 """ 1705 test_package_1 = make_package("test_package_1", version="0.1") 1706 test_package_2 = make_package( 1707 "test_package_2", version="0.1", install_requires=["test-package-1"] 1708 ) 1709 1710 dists_dir = tmpdir / "dists" 1711 1712 for pkg in (test_package_1, test_package_2): 1713 make_sdist(pkg, dists_dir) 1714 1715 with open("requirements.in", "w") as reqs_in: 1716 reqs_in.write(f"file:{test_package_2}\n") 1717 reqs_in.write(f"file:{test_package_2}#egg=test-package-2\n") 1718 1719 if output_content: 1720 with open("requirements.txt", "w") as reqs_out: 1721 reqs_out.write(output_content) 1722 1723 out = runner.invoke(cli, ["--find-links", str(dists_dir)]) 1724 1725 assert out.exit_code == 0, out 1726 assert str(test_package_2) in out.stderr 1727 assert "test-package-1==0.1" in out.stderr 1728 1729 1730def test_combine_extras(pip_conf, runner, make_package): 1731 """ 1732 Ensure that multiple declarations of a dependency that specify different 1733 extras produces a requirement for that package with the union of the extras 1734 """ 1735 package_with_extras = make_package( 1736 "package_with_extras", 1737 extras_require={ 1738 "extra1": ["small-fake-a==0.1"], 1739 "extra2": ["small-fake-b==0.1"], 1740 }, 1741 ) 1742 1743 with open("requirements.in", "w") as req_in: 1744 req_in.writelines( 1745 [ 1746 "-r ./requirements-second.in\n", 1747 f"{package_with_extras}[extra1]", 1748 ] 1749 ) 1750 1751 with open("requirements-second.in", "w") as req_sec_in: 1752 req_sec_in.write(f"{package_with_extras}[extra2]") 1753 1754 out = runner.invoke(cli, ["-n"]) 1755 1756 assert out.exit_code == 0 1757 assert "package-with-extras" in out.stderr 1758 assert "small-fake-a==" in out.stderr 1759 assert "small-fake-b==" in out.stderr 1760 1761 1762@pytest.mark.parametrize( 1763 ("pkg2_install_requires", "req_in_content", "out_expected_content"), 1764 ( 1765 pytest.param( 1766 "", 1767 ["test-package-1===0.1.0\n"], 1768 ["test-package-1===0.1.0"], 1769 id="pin package with ===", 1770 ), 1771 pytest.param( 1772 "", 1773 ["test-package-1==0.1.0\n"], 1774 ["test-package-1==0.1.0"], 1775 id="pin package with ==", 1776 ), 1777 pytest.param( 1778 "test-package-1==0.1.0", 1779 ["test-package-1===0.1.0\n", "test-package-2==0.1.0\n"], 1780 ["test-package-1===0.1.0", "test-package-2==0.1.0"], 1781 id="dep === pin preferred over == pin, main package == pin", 1782 ), 1783 pytest.param( 1784 "test-package-1==0.1.0", 1785 ["test-package-1===0.1.0\n", "test-package-2===0.1.0\n"], 1786 ["test-package-1===0.1.0", "test-package-2===0.1.0"], 1787 id="dep === pin preferred over == pin, main package === pin", 1788 ), 1789 pytest.param( 1790 "test-package-1==0.1.0", 1791 ["test-package-2===0.1.0\n"], 1792 ["test-package-1==0.1.0", "test-package-2===0.1.0"], 1793 id="dep == pin conserved, main package === pin", 1794 ), 1795 ), 1796) 1797def test_triple_equal_pinned_dependency_is_used( 1798 runner, 1799 make_package, 1800 make_wheel, 1801 tmpdir, 1802 pkg2_install_requires, 1803 req_in_content, 1804 out_expected_content, 1805): 1806 """ 1807 Test that pip-compile properly emits the pinned requirement with === 1808 torchvision 0.8.2 requires torch==1.7.1 which can resolve to versions with 1809 patches (e.g. torch 1.7.1+cu110), we want torch===1.7.1 without patches 1810 """ 1811 1812 dists_dir = tmpdir / "dists" 1813 1814 test_package_1 = make_package("test_package_1", version="0.1.0") 1815 make_wheel(test_package_1, dists_dir) 1816 1817 test_package_2 = make_package( 1818 "test_package_2", version="0.1.0", install_requires=[pkg2_install_requires] 1819 ) 1820 make_wheel(test_package_2, dists_dir) 1821 1822 with open("requirements.in", "w") as reqs_in: 1823 for line in req_in_content: 1824 reqs_in.write(line) 1825 1826 out = runner.invoke(cli, ["--find-links", str(dists_dir)]) 1827 1828 assert out.exit_code == 0, out 1829 for line in out_expected_content: 1830 assert line in out.stderr 1831 1832 1833METADATA_TEST_CASES = ( 1834 pytest.param( 1835 "setup.cfg", 1836 """ 1837 [metadata] 1838 name = sample_lib 1839 author = Vincent Driessen 1840 author_email = me@nvie.com 1841 1842 [options] 1843 packages = find: 1844 install_requires = 1845 small-fake-a==0.1 1846 small-fake-b==0.2 1847 1848 [options.extras_require] 1849 dev = 1850 small-fake-c==0.3 1851 small-fake-d==0.4 1852 test = 1853 small-fake-e==0.5 1854 small-fake-f==0.6 1855 """, 1856 id="setup.cfg", 1857 ), 1858 pytest.param( 1859 "setup.py", 1860 """ 1861 from setuptools import setup 1862 1863 setup( 1864 name="sample_lib", 1865 version=0.1, 1866 install_requires=["small-fake-a==0.1", "small-fake-b==0.2"], 1867 extras_require={ 1868 "dev": ["small-fake-c==0.3", "small-fake-d==0.4"], 1869 "test": ["small-fake-e==0.5", "small-fake-f==0.6"], 1870 }, 1871 ) 1872 """, 1873 id="setup.py", 1874 ), 1875 pytest.param( 1876 "pyproject.toml", 1877 """ 1878 [build-system] 1879 requires = ["flit_core >=2,<4"] 1880 build-backend = "flit_core.buildapi" 1881 1882 [tool.flit.metadata] 1883 module = "sample_lib" 1884 author = "Vincent Driessen" 1885 author-email = "me@nvie.com" 1886 1887 requires = ["small-fake-a==0.1", "small-fake-b==0.2"] 1888 1889 [tool.flit.metadata.requires-extra] 1890 dev = ["small-fake-c==0.3", "small-fake-d==0.4"] 1891 test = ["small-fake-e==0.5", "small-fake-f==0.6"] 1892 """, 1893 id="flit", 1894 ), 1895 pytest.param( 1896 "pyproject.toml", 1897 """ 1898 [build-system] 1899 requires = ["poetry_core>=1.0.0"] 1900 build-backend = "poetry.core.masonry.api" 1901 1902 [tool.poetry] 1903 name = "sample_lib" 1904 version = "0.1.0" 1905 description = "" 1906 authors = ["Vincent Driessen <me@nvie.com>"] 1907 1908 [tool.poetry.dependencies] 1909 python = "*" 1910 small-fake-a = "0.1" 1911 small-fake-b = "0.2" 1912 1913 small-fake-c = "0.3" 1914 small-fake-d = "0.4" 1915 small-fake-e = "0.5" 1916 small-fake-f = "0.6" 1917 1918 [tool.poetry.extras] 1919 dev = ["small-fake-c", "small-fake-d"] 1920 test = ["small-fake-e", "small-fake-f"] 1921 """, 1922 id="poetry", 1923 ), 1924) 1925 1926 1927@pytest.mark.network 1928@pytest.mark.parametrize(("fname", "content"), METADATA_TEST_CASES) 1929@pytest.mark.xfail(is_pypy, reason="https://github.com/jazzband/pip-tools/issues/1375") 1930def test_input_formats(fake_dists, runner, make_module, fname, content): 1931 """ 1932 Test different dependency formats as input file. 1933 """ 1934 meta_path = make_module(fname=fname, content=content) 1935 out = runner.invoke(cli, ["-n", "--find-links", fake_dists, meta_path]) 1936 assert out.exit_code == 0, out.stderr 1937 assert "small-fake-a==0.1" in out.stderr 1938 assert "small-fake-b==0.2" in out.stderr 1939 assert "small-fake-c" not in out.stderr 1940 assert "small-fake-d" not in out.stderr 1941 assert "small-fake-e" not in out.stderr 1942 assert "small-fake-f" not in out.stderr 1943 assert "extra ==" not in out.stderr 1944 1945 1946@pytest.mark.network 1947@pytest.mark.parametrize(("fname", "content"), METADATA_TEST_CASES) 1948@pytest.mark.xfail(is_pypy, reason="https://github.com/jazzband/pip-tools/issues/1375") 1949def test_one_extra(fake_dists, runner, make_module, fname, content): 1950 """ 1951 Test one `--extra` (dev) passed, other extras (test) must be ignored. 1952 """ 1953 meta_path = make_module(fname=fname, content=content) 1954 out = runner.invoke( 1955 cli, ["-n", "--extra", "dev", "--find-links", fake_dists, meta_path] 1956 ) 1957 assert out.exit_code == 0, out.stderr 1958 assert "small-fake-a==0.1" in out.stderr 1959 assert "small-fake-b==0.2" in out.stderr 1960 assert "small-fake-c==0.3" in out.stderr 1961 assert "small-fake-d==0.4" in out.stderr 1962 assert "small-fake-e" not in out.stderr 1963 assert "small-fake-f" not in out.stderr 1964 assert "extra ==" not in out.stderr 1965 1966 1967@pytest.mark.network 1968@pytest.mark.parametrize( 1969 "extra_opts", 1970 ( 1971 pytest.param(("--extra", "dev", "--extra", "test"), id="singular"), 1972 pytest.param(("--extra", "dev,test"), id="comma-separated"), 1973 ), 1974) 1975@pytest.mark.parametrize(("fname", "content"), METADATA_TEST_CASES) 1976@pytest.mark.xfail(is_pypy, reason="https://github.com/jazzband/pip-tools/issues/1375") 1977def test_multiple_extras(fake_dists, runner, make_module, fname, content, extra_opts): 1978 """ 1979 Test passing multiple `--extra` params. 1980 """ 1981 meta_path = make_module(fname=fname, content=content) 1982 out = runner.invoke( 1983 cli, 1984 [ 1985 "-n", 1986 *extra_opts, 1987 "--find-links", 1988 fake_dists, 1989 meta_path, 1990 ], 1991 ) 1992 assert out.exit_code == 0, out.stderr 1993 assert "small-fake-a==0.1" in out.stderr 1994 assert "small-fake-b==0.2" in out.stderr 1995 assert "small-fake-c==0.3" in out.stderr 1996 assert "small-fake-d==0.4" in out.stderr 1997 assert "small-fake-e==0.5" in out.stderr 1998 assert "small-fake-f==0.6" in out.stderr 1999 assert "extra ==" not in out.stderr 2000 2001 2002def test_extras_fail_with_requirements_in(runner, tmpdir): 2003 """ 2004 Test that passing `--extra` with `requirements.in` input file fails. 2005 """ 2006 path = os.path.join(tmpdir, "requirements.in") 2007 with open(path, "w") as stream: 2008 stream.write("\n") 2009 out = runner.invoke(cli, ["-n", "--extra", "something", path]) 2010 assert out.exit_code == 2 2011 exp = "--extra has effect only with setup.py and PEP-517 input formats" 2012 assert exp in out.stderr 2013 2014 2015def test_cli_compile_strip_extras(runner, make_package, make_sdist, tmpdir): 2016 """ 2017 Assures that --strip-extras removes mention of extras from output. 2018 """ 2019 test_package_1 = make_package( 2020 "test_package_1", version="0.1", extras_require={"more": "test_package_2"} 2021 ) 2022 test_package_2 = make_package( 2023 "test_package_2", 2024 version="0.1", 2025 ) 2026 dists_dir = tmpdir / "dists" 2027 2028 for pkg in (test_package_1, test_package_2): 2029 make_sdist(pkg, dists_dir) 2030 2031 with open("requirements.in", "w") as reqs_out: 2032 reqs_out.write("test_package_1[more]") 2033 2034 out = runner.invoke(cli, ["--strip-extras", "--find-links", str(dists_dir)]) 2035 2036 assert out.exit_code == 0, out 2037 assert "test-package-2==0.1" in out.stderr 2038 assert "[more]" not in out.stderr 2039