Skip to content

Commit f780fb3

Browse files
authored
Merge pull request #3996 from HypothesisWorks/create-pull-request/patch
Update pinned dependencies
2 parents 26ea571 + 99ba90f commit f780fb3

File tree

15 files changed

+143
-101
lines changed

15 files changed

+143
-101
lines changed

.github/workflows/main.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ jobs:
4949
- check-py313-cover
5050
- check-py313-nocover
5151
- check-py313-niche
52+
# - check-py314-cover
53+
# - check-py314-nocover
54+
# - check-py314-niche
5255
- check-quality
5356
## Skip all the (inactive/old) Rust and Ruby tests pending fixes
5457
# - lint-ruby
@@ -247,7 +250,7 @@ jobs:
247250
- name: Run tests
248251
run: |
249252
source .venv-pyodide/bin/activate
250-
python -m pytest hypothesis-python/tests/cover
253+
python -m pytest -p no:cacheprovider hypothesis-python/tests/cover
251254
252255
deploy:
253256
if: "github.event_name == 'push' && github.repository == 'HypothesisWorks/hypothesis'"

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ htmlcov
3434
build
3535
dist
3636
.doctrees/
37-
.venv/
37+
.v*/
3838

3939
# encrypted files
4040
secrets.tar

hypothesis-python/RELEASE.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
RELEASE_TYPE: patch
2+
3+
This patch fixes some introspection errors new in Python 3.11.9 and
4+
3.13.0b1, for the Ghostwriter and :func:`~hypothesis.strategies.from_type`.

hypothesis-python/setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ def local_file(name):
6060
"pytest": ["pytest>=4.6"],
6161
"dpcontracts": ["dpcontracts>=0.4"],
6262
"redis": ["redis>=3.0.0"],
63-
"crosshair": ["hypothesis-crosshair>=0.0.2", "crosshair-tool>=0.0.54"],
63+
"crosshair": ["hypothesis-crosshair>=0.0.4", "crosshair-tool>=0.0.54"],
6464
# zoneinfo is an odd one: every dependency is conditional, because they're
6565
# only necessary on old versions of Python or Windows systems or emscripten.
6666
"zoneinfo": [

hypothesis-python/src/hypothesis/extra/ghostwriter.py

Lines changed: 57 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -452,54 +452,72 @@ def _guess_strategy_by_argname(name: str) -> st.SearchStrategy:
452452
return st.nothing()
453453

454454

455+
def _get_params_builtin_fn(func: Callable) -> List[inspect.Parameter]:
456+
if (
457+
isinstance(func, (types.BuiltinFunctionType, types.BuiltinMethodType))
458+
and hasattr(func, "__doc__")
459+
and isinstance(func.__doc__, str)
460+
):
461+
# inspect.signature doesn't work on all builtin functions or methods.
462+
# In such cases, we can try to reconstruct simple signatures from the docstring.
463+
match = re.match(rf"^{func.__name__}\((.+?)\)", func.__doc__)
464+
if match is None:
465+
return []
466+
args = match.group(1).replace("[", "").replace("]", "")
467+
params = []
468+
# Even if the signature doesn't contain a /, we assume that arguments
469+
# are positional-only until shown otherwise - the / is often omitted.
470+
kind: inspect._ParameterKind = inspect.Parameter.POSITIONAL_ONLY
471+
for arg in args.split(", "):
472+
arg, *_ = arg.partition("=")
473+
arg = arg.strip()
474+
if arg == "/":
475+
kind = inspect.Parameter.POSITIONAL_OR_KEYWORD
476+
continue
477+
if arg.startswith("*") or arg == "...":
478+
kind = inspect.Parameter.KEYWORD_ONLY
479+
continue # we omit *varargs, if there are any
480+
if _iskeyword(arg.lstrip("*")) or not arg.lstrip("*").isidentifier():
481+
break # skip all subsequent params if this name is invalid
482+
params.append(inspect.Parameter(name=arg, kind=kind))
483+
return params
484+
return []
485+
486+
487+
def _get_params_ufunc(func: Callable) -> List[inspect.Parameter]:
488+
if _is_probably_ufunc(func):
489+
# `inspect.signature` results vary for ufunc objects, but we can work out
490+
# what the required parameters would look like if it was reliable.
491+
# Note that we use args named a, b, c... to match the `operator` module,
492+
# rather than x1, x2, x3... like the Numpy docs. Because they're pos-only
493+
# this doesn't make a runtime difference, and it's much nicer for use-cases
494+
# like `equivalent(numpy.add, operator.add)`.
495+
return [
496+
inspect.Parameter(name=name, kind=inspect.Parameter.POSITIONAL_ONLY)
497+
for name in ascii_lowercase[: func.nin] # type: ignore
498+
]
499+
return []
500+
501+
455502
def _get_params(func: Callable) -> Dict[str, inspect.Parameter]:
456503
"""Get non-vararg parameters of `func` as an ordered dict."""
457504
try:
458505
params = list(get_signature(func).parameters.values())
459506
except Exception:
460-
if (
461-
isinstance(func, (types.BuiltinFunctionType, types.BuiltinMethodType))
462-
and hasattr(func, "__doc__")
463-
and isinstance(func.__doc__, str)
464-
):
465-
# inspect.signature doesn't work on all builtin functions or methods.
466-
# In such cases, we can try to reconstruct simple signatures from the docstring.
467-
match = re.match(rf"^{func.__name__}\((.+?)\)", func.__doc__)
468-
if match is None:
469-
raise
470-
args = match.group(1).replace("[", "").replace("]", "")
471-
params = []
472-
# Even if the signature doesn't contain a /, we assume that arguments
473-
# are positional-only until shown otherwise - the / is often omitted.
474-
kind: inspect._ParameterKind = inspect.Parameter.POSITIONAL_ONLY
475-
for arg in args.split(", "):
476-
arg, *_ = arg.partition("=")
477-
arg = arg.strip()
478-
if arg == "/":
479-
kind = inspect.Parameter.POSITIONAL_OR_KEYWORD
480-
continue
481-
if arg.startswith("*") or arg == "...":
482-
kind = inspect.Parameter.KEYWORD_ONLY
483-
continue # we omit *varargs, if there are any
484-
if _iskeyword(arg.lstrip("*")) or not arg.lstrip("*").isidentifier():
485-
break # skip all subsequent params if this name is invalid
486-
params.append(inspect.Parameter(name=arg, kind=kind))
487-
488-
elif _is_probably_ufunc(func):
489-
# `inspect.signature` doesn't work on ufunc objects, but we can work out
490-
# what the required parameters would look like if it did.
491-
# Note that we use args named a, b, c... to match the `operator` module,
492-
# rather than x1, x2, x3... like the Numpy docs. Because they're pos-only
493-
# this doesn't make a runtime difference, and it's much nicer for use-cases
494-
# like `equivalent(numpy.add, operator.add)`.
495-
params = [
496-
inspect.Parameter(name=name, kind=inspect.Parameter.POSITIONAL_ONLY)
497-
for name in ascii_lowercase[: func.nin] # type: ignore
498-
]
507+
if params := _get_params_ufunc(func):
508+
pass
509+
elif params := _get_params_builtin_fn(func):
510+
pass
499511
else:
500512
# If we haven't managed to recover a signature through the tricks above,
501513
# we're out of ideas and should just re-raise the exception.
502514
raise
515+
else:
516+
# If the params we got look like an uninformative placeholder, try fallbacks.
517+
P = inspect.Parameter
518+
placeholder = [("args", P.VAR_POSITIONAL), ("kwargs", P.VAR_KEYWORD)]
519+
if [(p.name, p.kind) for p in params] == placeholder:
520+
params = _get_params_ufunc(func) or _get_params_builtin_fn(func) or params
503521
return _params_to_dict(params)
504522

505523

hypothesis-python/src/hypothesis/strategies/_internal/types.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -388,7 +388,12 @@ def is_generic_type(type_):
388388
)
389389

390390

391-
def _try_import_forward_ref(thing, bound): # pragma: no cover
391+
__EVAL_TYPE_TAKES_TYPE_PARAMS = (
392+
"type_params" in inspect.signature(typing._eval_type).parameters # type: ignore
393+
)
394+
395+
396+
def _try_import_forward_ref(thing, bound, *, type_params): # pragma: no cover
392397
"""
393398
Tries to import a real bound type from ``TypeVar`` bound to a ``ForwardRef``.
394399
@@ -397,7 +402,10 @@ def _try_import_forward_ref(thing, bound): # pragma: no cover
397402
because we can only cover each path in a separate python version.
398403
"""
399404
try:
400-
return typing._eval_type(bound, vars(sys.modules[thing.__module__]), None)
405+
kw = {"globalns": vars(sys.modules[thing.__module__]), "localns": None}
406+
if __EVAL_TYPE_TAKES_TYPE_PARAMS:
407+
kw["type_params"] = type_params
408+
return typing._eval_type(bound, **kw)
401409
except (KeyError, AttributeError, NameError):
402410
# We fallback to `ForwardRef` instance, you can register it as a type as well:
403411
# >>> from typing import ForwardRef
@@ -1030,7 +1038,9 @@ def resolve_TypeVar(thing):
10301038
if getattr(thing, "__bound__", None) is not None:
10311039
bound = thing.__bound__
10321040
if isinstance(bound, typing.ForwardRef):
1033-
bound = _try_import_forward_ref(thing, bound)
1041+
# TODO: on Python 3.13 and later, we should work out what type_params
1042+
# could be part of this type, and pass them in here.
1043+
bound = _try_import_forward_ref(thing, bound, type_params=())
10341044
strat = unwrap_strategies(st.from_type(bound))
10351045
if not isinstance(strat, OneOfStrategy):
10361046
return strat

hypothesis-python/tests/cover/test_interactive_example.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ def test_selftests_exception_contains_note(pytester):
101101

102102
pytester.makeconftest("from tests.conftest import *")
103103
result = pytester.runpytest_inprocess(
104-
pytester.makepyfile(EXAMPLE_GENERATING_TEST)
104+
pytester.makepyfile(EXAMPLE_GENERATING_TEST), "-p", "no:cacheprovider"
105105
)
106106
assert "helper methods in tests.common.debug" in "\n".join(result.outlines)
107107

hypothesis-python/tests/cover/test_settings.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -230,13 +230,13 @@ def test_settings_alone():
230230
"""
231231

232232

233-
def test_settings_alone(testdir):
234-
script = testdir.makepyfile(TEST_SETTINGS_ALONE)
235-
result = testdir.runpytest_inprocess(script)
233+
def test_settings_alone(pytester):
234+
# Disable cacheprovider, since we don't need it and it's flaky on pyodide
235+
script = pytester.makepyfile(TEST_SETTINGS_ALONE)
236+
result = pytester.runpytest_inprocess(script, "-p", "no:cacheprovider")
236237
out = "\n".join(result.stdout.lines)
237-
assert (
238-
"Using `@settings` on a test without `@given` is completely pointless." in out
239-
)
238+
msg = "Using `@settings` on a test without `@given` is completely pointless."
239+
assert msg in out
240240
assert "InvalidArgument" in out
241241
assert result.ret == 1
242242

hypothesis-python/tests/ghostwriter/recorded/magic_builtins.txt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@ def test_fuzz_abs(x):
1111
abs(x)
1212

1313

14+
@given(async_iterable=st.nothing())
15+
def test_fuzz_aiter(async_iterable):
16+
aiter(async_iterable)
17+
18+
1419
@given(iterable=st.one_of(st.iterables(st.integers()), st.iterables(st.text())))
1520
def test_fuzz_all(iterable):
1621
all(iterable)
@@ -147,7 +152,7 @@ def test_fuzz_id(obj):
147152
id(obj)
148153

149154

150-
@given(prompt=st.none())
155+
@given(prompt=st.just(""))
151156
def test_fuzz_input(prompt):
152157
input(prompt)
153158

hypothesis-python/tests/ghostwriter/test_expected_output.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -269,8 +269,8 @@ def sequence_from_collections(items: CollectionsSequence[int]) -> int:
269269
("magic_builtins", ghostwriter.magic(builtins)),
270270
marks=[
271271
pytest.mark.skipif(
272-
sys.version_info[:2] not in [(3, 8), (3, 9)],
273-
reason="compile arg new in 3.8, aiter and anext new in 3.10",
272+
sys.version_info[:2] != (3, 10),
273+
reason="often small changes",
274274
)
275275
],
276276
),

requirements/coverage.txt

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ click==8.1.7
1616
# via
1717
# -r requirements/coverage.in
1818
# black
19-
coverage[toml]==7.5.0
19+
coverage[toml]==7.5.1
2020
# via pytest-cov
2121
dpcontracts==0.6.0
2222
# via -r requirements/coverage.in
@@ -26,7 +26,7 @@ exceptiongroup==1.2.1 ; python_version < "3.11"
2626
# pytest
2727
execnet==2.1.1
2828
# via pytest-xdist
29-
fakeredis==2.22.0
29+
fakeredis==2.23.2
3030
# via -r requirements/coverage.in
3131
iniconfig==2.0.0
3232
# via pytest
@@ -51,13 +51,13 @@ pathspec==0.12.1
5151
# via black
5252
pexpect==4.9.0
5353
# via -r requirements/test.in
54-
platformdirs==4.2.1
54+
platformdirs==4.2.2
5555
# via black
5656
pluggy==1.5.0
5757
# via pytest
5858
ptyprocess==0.7.0
5959
# via pexpect
60-
pyarrow==16.0.0
60+
pyarrow==16.1.0
6161
# via -r requirements/coverage.in
6262
pytest==8.2.0
6363
# via
@@ -66,7 +66,7 @@ pytest==8.2.0
6666
# pytest-xdist
6767
pytest-cov @ git+https://github.com/pytest-dev/pytest-cov.git@9757222e2e044361e70125ebdd96e5eb87395983
6868
# via -r requirements/coverage.in
69-
pytest-xdist==3.5.0
69+
pytest-xdist==3.6.1
7070
# via -r requirements/test.in
7171
python-dateutil==2.9.0.post0
7272
# via
@@ -91,9 +91,10 @@ tomli==2.0.1
9191
# black
9292
# coverage
9393
# pytest
94-
typing-extensions==4.11.0
94+
typing-extensions==4.12.0rc1
9595
# via
9696
# -r requirements/coverage.in
9797
# black
98+
# fakeredis
9899
tzdata==2024.1
99100
# via pandas

0 commit comments

Comments
 (0)