From 9c98d2000f67bae46cd72490443b11663fb35bd4 Mon Sep 17 00:00:00 2001 From: CI on behalf of the Hypothesis team Date: Mon, 21 Oct 2024 01:07:08 -0700 Subject: [PATCH 1/7] Update pinned dependencies --- requirements/coverage.txt | 6 +++--- requirements/fuzzing.txt | 12 ++++++------ requirements/tools.txt | 30 +++++++++++++++--------------- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/requirements/coverage.txt b/requirements/coverage.txt index 39d171f05a..9a7f8280c4 100644 --- a/requirements/coverage.txt +++ b/requirements/coverage.txt @@ -12,13 +12,13 @@ attrs==24.1.0 # via # -r requirements/test.in # hypothesis (hypothesis-python/setup.py) -black==24.8.0 +black==24.10.0 # via -r requirements/coverage.in click==8.1.7 # via # -r requirements/coverage.in # black -coverage[toml]==7.6.1 +coverage[toml]==7.6.2 # via pytest-cov dpcontracts==0.6.0 # via -r requirements/coverage.in @@ -34,7 +34,7 @@ iniconfig==2.0.0 # via pytest lark==1.2.2 # via -r requirements/coverage.in -libcst==1.4.0 +libcst==1.5.0 # via -r requirements/coverage.in mypy-extensions==1.0.0 # via black diff --git a/requirements/fuzzing.txt b/requirements/fuzzing.txt index bf5ca6f177..08d4f95a3a 100644 --- a/requirements/fuzzing.txt +++ b/requirements/fuzzing.txt @@ -13,7 +13,7 @@ attrs==24.1.0 # -r requirements/test.in # hypothesis # hypothesis (hypothesis-python/setup.py) -black==24.8.0 +black==24.10.0 # via # -r requirements/coverage.in # hypofuzz @@ -22,7 +22,7 @@ blinker==1.8.2 # via flask certifi==2024.8.30 # via requests -charset-normalizer==3.3.2 +charset-normalizer==3.4.0 # via requests click==8.1.7 # via @@ -30,7 +30,7 @@ click==8.1.7 # black # flask # hypothesis -coverage[toml]==7.6.1 +coverage[toml]==7.6.2 # via # hypofuzz # pytest-cov @@ -57,7 +57,7 @@ flask==3.0.3 # via dash hypofuzz==24.9.1 # via -r requirements/fuzzing.in -hypothesis[cli]==6.112.2 +hypothesis[cli]==6.115.0 # via hypofuzz idna==3.10 # via requests @@ -71,13 +71,13 @@ jinja2==3.1.4 # via flask lark==1.2.2 # via -r requirements/coverage.in -libcst==1.4.0 +libcst==1.5.0 # via # -r requirements/coverage.in # hypofuzz markdown-it-py==3.0.0 # via rich -markupsafe==2.1.5 +markupsafe==3.0.1 # via # jinja2 # werkzeug diff --git a/requirements/tools.txt b/requirements/tools.txt index 5d18b33a31..9eb6aae910 100644 --- a/requirements/tools.txt +++ b/requirements/tools.txt @@ -4,7 +4,7 @@ # # ./build.sh upgrade-requirements # -alabaster==0.7.16 +alabaster==1.0.0 # via sphinx anyio==4.6.0 # via watchfiles @@ -22,11 +22,11 @@ backports-tarfile==1.2.0 # via jaraco-context beautifulsoup4==4.12.3 # via sphinx-codeautolink -black==24.8.0 +black==24.10.0 # via shed blinker==1.8.2 # via pelican -build==1.2.2 +build==1.2.2.post1 # via pip-tools cachetools==5.5.0 # via tox @@ -36,7 +36,7 @@ cffi==1.17.1 # via cryptography chardet==5.2.0 # via tox -charset-normalizer==3.3.2 +charset-normalizer==3.4.0 # via requests click==8.1.7 # via @@ -48,7 +48,7 @@ colorama==0.4.6 # via tox com2ann==0.3.0 # via shed -coverage==7.6.1 +coverage==7.6.2 # via -r requirements/tools.in cryptography==43.0.1 # via @@ -57,11 +57,11 @@ cryptography==43.0.1 # types-redis decorator==5.1.1 # via ipython -distlib==0.3.8 +distlib==0.3.9 # via virtualenv -django==5.1.1 +django==5.1.2 # via -r requirements/tools.in -docutils==0.20.1 +docutils==0.21.2 # via # pelican # readme-renderer @@ -123,7 +123,7 @@ keyring==25.4.1 # via twine lark==1.2.2 # via -r requirements/tools.in -libcst==1.4.0 +libcst==1.5.0 # via # -r requirements/tools.in # shed @@ -131,7 +131,7 @@ markdown==3.7 # via pelican markdown-it-py==3.0.0 # via rich -markupsafe==2.1.5 +markupsafe==3.0.1 # via jinja2 matplotlib-inline==0.1.7 # via ipython @@ -207,7 +207,7 @@ pyproject-hooks==1.2.0 # via # build # pip-tools -pyright==1.1.383 +pyright==1.1.384 # via -r requirements/tools.in pytest==8.3.3 # via -r requirements/tools.in @@ -217,13 +217,13 @@ python-dateutil==2.9.0.post0 # pelican pytz==2024.2 # via feedgenerator -pyupgrade==3.17.0 +pyupgrade==3.18.0 # via shed pyyaml==6.0.2 # via # libcst # sphinx-jsonschema -readme-renderer==43.0 +readme-renderer==44.0 # via twine requests==2.32.3 # via @@ -260,7 +260,7 @@ sortedcontainers==2.4.0 # via hypothesis (hypothesis-python/setup.py) soupsieve==2.6 # via beautifulsoup4 -sphinx==7.4.7 +sphinx==8.1.2 # via # -r requirements/tools.in # sphinx-codeautolink @@ -273,7 +273,7 @@ sphinx-hoverxref==1.4.1 # via -r requirements/tools.in sphinx-jsonschema==1.19.1 # via -r requirements/tools.in -sphinx-rtd-theme==2.0.0 +sphinx-rtd-theme==3.0.1 # via -r requirements/tools.in sphinx-selective-exclude==1.0.3 # via -r requirements/tools.in From 5531ebf32daec404c823d3af287c46046db1eea0 Mon Sep 17 00:00:00 2001 From: Zac Hatfield-Dodds Date: Mon, 21 Oct 2024 01:07:08 -0700 Subject: [PATCH 2/7] Update typecheck tests --- hypothesis-python/RELEASE.rst | 3 ++ hypothesis-python/docs/conf.py | 4 --- .../strategies/_internal/ipaddress.py | 2 +- requirements/tools.in | 2 +- requirements/tools.txt | 28 +++++++++++++------ tooling/src/hypothesistooling/__main__.py | 2 +- whole_repo_tests/revealed_types.py | 6 ++++ whole_repo_tests/test_mypy.py | 4 +-- whole_repo_tests/test_pyright.py | 16 +++-------- 9 files changed, 36 insertions(+), 31 deletions(-) create mode 100644 hypothesis-python/RELEASE.rst diff --git a/hypothesis-python/RELEASE.rst b/hypothesis-python/RELEASE.rst new file mode 100644 index 0000000000..f239c2e371 --- /dev/null +++ b/hypothesis-python/RELEASE.rst @@ -0,0 +1,3 @@ +RELEASE_TYPE: patch + +This patch updates some internal type annotations. diff --git a/hypothesis-python/docs/conf.py b/hypothesis-python/docs/conf.py index ba605b465c..8fad25d5b6 100644 --- a/hypothesis-python/docs/conf.py +++ b/hypothesis-python/docs/conf.py @@ -14,8 +14,6 @@ import types from pathlib import Path -import sphinx_rtd_theme - root = Path(__file__).parent.parent sys.path.append(str(root / "src")) @@ -152,8 +150,6 @@ def setup(app): html_theme = "sphinx_rtd_theme" -html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] - html_static_path = ["_static"] html_css_files = ["better-signatures.css", "wrap-in-tables.css"] diff --git a/hypothesis-python/src/hypothesis/strategies/_internal/ipaddress.py b/hypothesis-python/src/hypothesis/strategies/_internal/ipaddress.py index 75aaaba8d7..0f5fb1bccc 100644 --- a/hypothesis-python/src/hypothesis/strategies/_internal/ipaddress.py +++ b/hypothesis-python/src/hypothesis/strategies/_internal/ipaddress.py @@ -115,4 +115,4 @@ def ip_addresses( if v not in (None, network.version): raise InvalidArgument(f"{v=} is incompatible with {network=}") addr_type = IPv4Address if network.version == 4 else IPv6Address - return integers(int(network[0]), int(network[-1])).map(addr_type) # type: ignore + return integers(int(network[0]), int(network[-1])).map(addr_type) diff --git a/requirements/tools.in b/requirements/tools.in index b5905f20d2..39a3d269a8 100644 --- a/requirements/tools.in +++ b/requirements/tools.in @@ -10,7 +10,6 @@ numpy pelican[markdown] pip-tools pyright -pytest python-dateutil requests restructuredtext-lint @@ -28,3 +27,4 @@ types-click types-pytz types-redis typing-extensions +-r test.in diff --git a/requirements/tools.txt b/requirements/tools.txt index 9eb6aae910..668d8fef80 100644 --- a/requirements/tools.txt +++ b/requirements/tools.txt @@ -6,14 +6,16 @@ # alabaster==1.0.0 # via sphinx -anyio==4.6.0 +anyio==4.6.2.post1 # via watchfiles asgiref==3.8.1 # via django asttokens==2.4.1 # via stack-data -attrs==24.2.0 - # via hypothesis (hypothesis-python/setup.py) +attrs==24.1.0 + # via + # -r requirements/test.in + # hypothesis (hypothesis-python/setup.py) autoflake==2.3.1 # via shed babel==2.16.0 @@ -48,7 +50,7 @@ colorama==0.4.6 # via tox com2ann==0.3.0 # via shed -coverage==7.6.2 +coverage==7.6.3 # via -r requirements/tools.in cryptography==43.0.1 # via @@ -77,6 +79,8 @@ exceptiongroup==1.2.2 ; python_version < "3.11" # hypothesis (hypothesis-python/setup.py) # ipython # pytest +execnet==2.1.1 + # via pytest-xdist executing==2.1.0 # via stack-data feedgenerator==2.1.0 @@ -141,7 +145,7 @@ more-itertools==10.5.0 # via # jaraco-classes # jaraco-functools -mypy==1.11.2 +mypy==1.12.0 # via -r requirements/tools.in mypy-extensions==1.0.0 # via @@ -170,7 +174,9 @@ pathspec==0.12.1 pelican[markdown]==4.10.1 # via -r requirements/tools.in pexpect==4.9.0 - # via ipython + # via + # -r requirements/test.in + # ipython pip-tools==7.4.1 # via -r requirements/tools.in pkginfo==1.10.0 @@ -210,7 +216,11 @@ pyproject-hooks==1.2.0 pyright==1.1.384 # via -r requirements/tools.in pytest==8.3.3 - # via -r requirements/tools.in + # via + # -r requirements/test.in + # pytest-xdist +pytest-xdist==3.6.1 + # via -r requirements/test.in python-dateutil==2.9.0.post0 # via # -r requirements/tools.in @@ -260,7 +270,7 @@ sortedcontainers==2.4.0 # via hypothesis (hypothesis-python/setup.py) soupsieve==2.6 # via beautifulsoup4 -sphinx==8.1.2 +sphinx==8.1.3 # via # -r requirements/tools.in # sphinx-codeautolink @@ -328,7 +338,7 @@ types-pytz==2024.2.0.20241003 # via -r requirements/tools.in types-redis==4.6.0.20241004 # via -r requirements/tools.in -types-setuptools==75.1.0.20240917 +types-setuptools==75.1.0.20241014 # via types-cffi typing-extensions==4.12.2 # via diff --git a/tooling/src/hypothesistooling/__main__.py b/tooling/src/hypothesistooling/__main__.py index 6eb9385104..91244f236a 100644 --- a/tooling/src/hypothesistooling/__main__.py +++ b/tooling/src/hypothesistooling/__main__.py @@ -548,7 +548,7 @@ def check_whole_repo_tests(*args): ) if not args: - args = [tools.REPO_TESTS] + args = ["-n", "auto", tools.REPO_TESTS] subprocess.check_call([sys.executable, "-m", "pytest", *args]) diff --git a/whole_repo_tests/revealed_types.py b/whole_repo_tests/revealed_types.py index 08fa706afc..6e2e961fb5 100644 --- a/whole_repo_tests/revealed_types.py +++ b/whole_repo_tests/revealed_types.py @@ -8,6 +8,12 @@ # v. 2.0. If a copy of the MPL was not distributed with this file, You can # obtain one at https://mozilla.org/MPL/2.0/. +import re + +from hypothesistooling.__main__ import PYTHONS as pythons_map + +PYTHON_VERSIONS = [v for v in pythons_map if re.fullmatch(r"3\.\d\d?", v)] + try: from numpy import __version__ as np_version except ImportError: diff --git a/whole_repo_tests/test_mypy.py b/whole_repo_tests/test_mypy.py index 8022c3f32b..189e2a600a 100644 --- a/whole_repo_tests/test_mypy.py +++ b/whole_repo_tests/test_mypy.py @@ -16,9 +16,7 @@ from hypothesistooling.projects.hypothesispython import PYTHON_SRC from hypothesistooling.scripts import pip_tool, tool_path -from .revealed_types import NUMPY_REVEALED_TYPES, REVEALED_TYPES - -PYTHON_VERSIONS = ["3.8", "3.9", "3.10", "3.11"] +from .revealed_types import NUMPY_REVEALED_TYPES, PYTHON_VERSIONS, REVEALED_TYPES def test_mypy_passes_on_hypothesis(): diff --git a/whole_repo_tests/test_pyright.py b/whole_repo_tests/test_pyright.py index 0ae4384f99..6e62be06ff 100644 --- a/whole_repo_tests/test_pyright.py +++ b/whole_repo_tests/test_pyright.py @@ -23,9 +23,7 @@ from hypothesistooling.projects.hypothesispython import HYPOTHESIS_PYTHON, PYTHON_SRC from hypothesistooling.scripts import pip_tool, tool_path -from .revealed_types import NUMPY_REVEALED_TYPES, REVEALED_TYPES - -PYTHON_VERSIONS = ["3.7", "3.8", "3.9", "3.10", "3.11"] +from .revealed_types import NUMPY_REVEALED_TYPES, PYTHON_VERSIONS, REVEALED_TYPES @pytest.mark.skip( @@ -83,15 +81,9 @@ def f(): _write_config( tmp_path, {"typeCheckingMode": "strict", "pythonVersion": python_version} ) - assert ( - sum( - e["message"].startswith( - 'Argument of type "Literal[1]" cannot be assigned to parameter "_given_arguments"' - ) - for e in _get_pyright_errors(file) - ) - == 1 - ) + errors = _get_pyright_errors(file) + msg = 'Argument of type "Literal[1]" cannot be assigned to parameter "_given_arguments"' + assert sum(e["message"].startswith(msg) for e in errors) == 1, errors def test_pyright_issue_3296(tmp_path: Path): From e088270aee15329061b06384057c913f7081e295 Mon Sep 17 00:00:00 2001 From: Zac Hatfield-Dodds Date: Mon, 21 Oct 2024 01:07:08 -0700 Subject: [PATCH 3/7] skip update for now --- hypothesis-python/RELEASE.rst | 3 --- .../src/hypothesis/strategies/_internal/ipaddress.py | 2 +- requirements/tools.txt | 2 +- 3 files changed, 2 insertions(+), 5 deletions(-) delete mode 100644 hypothesis-python/RELEASE.rst diff --git a/hypothesis-python/RELEASE.rst b/hypothesis-python/RELEASE.rst deleted file mode 100644 index f239c2e371..0000000000 --- a/hypothesis-python/RELEASE.rst +++ /dev/null @@ -1,3 +0,0 @@ -RELEASE_TYPE: patch - -This patch updates some internal type annotations. diff --git a/hypothesis-python/src/hypothesis/strategies/_internal/ipaddress.py b/hypothesis-python/src/hypothesis/strategies/_internal/ipaddress.py index 0f5fb1bccc..75aaaba8d7 100644 --- a/hypothesis-python/src/hypothesis/strategies/_internal/ipaddress.py +++ b/hypothesis-python/src/hypothesis/strategies/_internal/ipaddress.py @@ -115,4 +115,4 @@ def ip_addresses( if v not in (None, network.version): raise InvalidArgument(f"{v=} is incompatible with {network=}") addr_type = IPv4Address if network.version == 4 else IPv6Address - return integers(int(network[0]), int(network[-1])).map(addr_type) + return integers(int(network[0]), int(network[-1])).map(addr_type) # type: ignore diff --git a/requirements/tools.txt b/requirements/tools.txt index 668d8fef80..bbb0ec3c8f 100644 --- a/requirements/tools.txt +++ b/requirements/tools.txt @@ -145,7 +145,7 @@ more-itertools==10.5.0 # via # jaraco-classes # jaraco-functools -mypy==1.12.0 +mypy==1.11.2 # via -r requirements/tools.in mypy-extensions==1.0.0 # via From 65f52a239cb127f46101aa9abb8bd5a8098d3cbc Mon Sep 17 00:00:00 2001 From: Zac Hatfield-Dodds Date: Mon, 21 Oct 2024 01:07:08 -0700 Subject: [PATCH 4/7] skip py3.14 tests for now --- .github/workflows/main.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 32df1a5431..56c4ebd3d8 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -48,9 +48,9 @@ jobs: # - check-py313t-cover # - check-py313t-nocover # - check-py313t-niche - - check-py314-cover - - check-py314-nocover - - check-py314-niche + # - check-py314-cover + # - check-py314-nocover + # - check-py314-niche # - check-py314t-cover # - check-py314t-nocover # - check-py314t-niche From f011f1ac1299a34e280b928939002730e2b7684d Mon Sep 17 00:00:00 2001 From: Zac Hatfield-Dodds Date: Mon, 21 Oct 2024 01:07:08 -0700 Subject: [PATCH 5/7] work around mypy issues --- whole_repo_tests/test_mypy.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/whole_repo_tests/test_mypy.py b/whole_repo_tests/test_mypy.py index 189e2a600a..08c684f64f 100644 --- a/whole_repo_tests/test_mypy.py +++ b/whole_repo_tests/test_mypy.py @@ -326,7 +326,8 @@ def test_stateful_target_params_mutually_exclusive(tmp_path, decorator): "target_args", [ "target=b1", - "targets=(b1,)", + # FIXME: temporary workaround for mypy bug, see hypothesis/pull/4136 + pytest.param("targets=(b1,)", marks=pytest.mark.xfail(strict=False)), "targets=(b1, b2)", "", ], @@ -563,7 +564,14 @@ def test_bar(x: str) -> None: assert_mypy_errors(f, [], python_version=python_version) -@pytest.mark.parametrize("python_version", PYTHON_VERSIONS) +@pytest.mark.parametrize( + "python_version", + [ + # FIXME: temporary workaround for mypy bug, see hypothesis/pull/4136 + pytest.param(v, marks=[pytest.mark.xfail(strict=False)] * (v == "3.13")) + for v in PYTHON_VERSIONS + ], +) def test_given_only_allows_strategies(tmp_path, python_version): f = tmp_path / "check_mypy_given_expects_strategies.py" f.write_text( From 6f1baec5dedbfee7a9d773b17663942b21ac40e9 Mon Sep 17 00:00:00 2001 From: Zac Hatfield-Dodds Date: Mon, 21 Oct 2024 01:07:08 -0700 Subject: [PATCH 6/7] fix from_type(type) regression --- hypothesis-python/RELEASE.rst | 4 ++++ .../src/hypothesis/strategies/_internal/types.py | 4 +--- hypothesis-python/tests/nocover/test_from_type_recipe.py | 6 ++++++ 3 files changed, 11 insertions(+), 3 deletions(-) create mode 100644 hypothesis-python/RELEASE.rst diff --git a/hypothesis-python/RELEASE.rst b/hypothesis-python/RELEASE.rst new file mode 100644 index 0000000000..edfec530cc --- /dev/null +++ b/hypothesis-python/RELEASE.rst @@ -0,0 +1,4 @@ +RELEASE_TYPE: patch + +This patch restores diversity to the outputs of +:func:`from_type(type) ` (:issue:`4144`). diff --git a/hypothesis-python/src/hypothesis/strategies/_internal/types.py b/hypothesis-python/src/hypothesis/strategies/_internal/types.py index 6dea780c95..4fd4829136 100644 --- a/hypothesis-python/src/hypothesis/strategies/_internal/types.py +++ b/hypothesis-python/src/hypothesis/strategies/_internal/types.py @@ -822,9 +822,7 @@ def really_inner(thing): @register("Type") @register("Type", module=typing_extensions) def resolve_Type(thing): - if getattr(thing, "__args__", None) is None: - return st.just(type) - elif get_args(thing) == (): # pragma: no cover + if getattr(thing, "__args__", None) is None or get_args(thing) == (): return _fallback_type_strategy args = (thing.__args__[0],) if is_a_union(args[0]): diff --git a/hypothesis-python/tests/nocover/test_from_type_recipe.py b/hypothesis-python/tests/nocover/test_from_type_recipe.py index d640ee6816..ae677e6e29 100644 --- a/hypothesis-python/tests/nocover/test_from_type_recipe.py +++ b/hypothesis-python/tests/nocover/test_from_type_recipe.py @@ -11,6 +11,8 @@ from hypothesis import given, strategies as st from hypothesis.strategies._internal.types import _global_type_lookup +from tests.common.debug import find_any + TYPES = sorted( ( x @@ -39,3 +41,7 @@ def everything_except(excluded_types): def test_recipe_for_everything_except(excluded_types, data): value = data.draw(everything_except(excluded_types)) assert not isinstance(value, excluded_types) + + +def test_issue_4144_regression(): + find_any(everything_except(()), lambda t: t is not type) From a373e61e6153ce6ce486062f9e1f942a0ea024a0 Mon Sep 17 00:00:00 2001 From: Zac Hatfield-Dodds Date: Mon, 21 Oct 2024 01:32:23 -0700 Subject: [PATCH 7/7] fix typecheck again --- whole_repo_tests/test_mypy.py | 12 +++--------- whole_repo_tests/test_pyright.py | 9 ++++++++- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/whole_repo_tests/test_mypy.py b/whole_repo_tests/test_mypy.py index 08c684f64f..8045e000bf 100644 --- a/whole_repo_tests/test_mypy.py +++ b/whole_repo_tests/test_mypy.py @@ -260,7 +260,8 @@ def test_stateful_bundle_generic_type(tmp_path): f.write_text( "from hypothesis.stateful import Bundle\n" "b: Bundle[int] = Bundle('test')\n" - "reveal_type(b.example())\n", + "x = b.example()\n" + "reveal_type(x)\n", encoding="utf-8", ) got = get_mypy_analysed_type(f) @@ -564,14 +565,7 @@ def test_bar(x: str) -> None: assert_mypy_errors(f, [], python_version=python_version) -@pytest.mark.parametrize( - "python_version", - [ - # FIXME: temporary workaround for mypy bug, see hypothesis/pull/4136 - pytest.param(v, marks=[pytest.mark.xfail(strict=False)] * (v == "3.13")) - for v in PYTHON_VERSIONS - ], -) +@pytest.mark.parametrize("python_version", PYTHON_VERSIONS) def test_given_only_allows_strategies(tmp_path, python_version): f = tmp_path / "check_mypy_given_expects_strategies.py" f.write_text( diff --git a/whole_repo_tests/test_pyright.py b/whole_repo_tests/test_pyright.py index 6e62be06ff..d3afef4be1 100644 --- a/whole_repo_tests/test_pyright.py +++ b/whole_repo_tests/test_pyright.py @@ -63,7 +63,14 @@ def test_bar(x: str): assert _get_pyright_errors(file) == [] -@pytest.mark.parametrize("python_version", PYTHON_VERSIONS) +@pytest.mark.parametrize( + "python_version", + [ + # FIXME: temporary workaround, see hypothesis/pull/4136 + pytest.param(v, marks=[pytest.mark.xfail(strict=False)] * (v == "3.13")) + for v in PYTHON_VERSIONS + ], +) def test_given_only_allows_strategies(tmp_path: Path, python_version: str): file = tmp_path / "test.py" file.write_text(