1{
2 lib,
3 buildPythonPackage,
4 isPyPy,
5 fetchFromGitHub,
6 setuptools,
7 attrs,
8 exceptiongroup,
9 pexpect,
10 doCheck ? true,
11 pytestCheckHook,
12 pytest-xdist,
13 python,
14 sortedcontainers,
15 stdenv,
16 pythonAtLeast,
17 pythonOlder,
18 sphinxHook,
19 sphinx-rtd-theme,
20 sphinx-hoverxref,
21 sphinx-codeautolink,
22 tzdata,
23}:
24
25buildPythonPackage rec {
26 pname = "hypothesis";
27 version = "6.136.9";
28 pyproject = true;
29
30 disabled = pythonOlder "3.9";
31
32 src = fetchFromGitHub {
33 owner = "HypothesisWorks";
34 repo = "hypothesis";
35 tag = "hypothesis-python-${version}";
36 hash = "sha256-Q1wxIJwAYKZ0x6c85CJSGgcdKw9a3xFw8YpJROElSNU=";
37 };
38
39 # I tried to package sphinx-selective-exclude, but it throws
40 # error about "module 'sphinx' has no attribute 'directives'".
41 #
42 # It probably has to do with monkey-patching internals of Sphinx.
43 # On bright side, this extension does not introduces new commands,
44 # only changes "::only" command, so we probably okay with stock
45 # implementation.
46 #
47 # I wonder how upstream of "hypothesis" builds documentation.
48 postPatch = ''
49 sed -i -e '/sphinx_selective_exclude.eager_only/ d' docs/conf.py
50 '';
51
52 postUnpack = "sourceRoot=$sourceRoot/hypothesis-python";
53
54 build-system = [ setuptools ];
55
56 dependencies = [
57 attrs
58 sortedcontainers
59 ]
60 ++ lib.optionals (pythonOlder "3.11") [ exceptiongroup ];
61
62 nativeCheckInputs = [
63 pexpect
64 pytest-xdist
65 pytestCheckHook
66 ]
67 ++ lib.optionals isPyPy [ tzdata ];
68
69 inherit doCheck;
70
71 # tox.ini changes how pytest runs and breaks it.
72 # Activate the CI profile (similar to setupHook below)
73 # by setting HYPOTHESIS_PROFILE [1].
74 #
75 # [1]: https://github.com/HypothesisWorks/hypothesis/blob/hypothesis-python-6.130.9/hypothesis-python/tests/common/setup.py#L78
76 preCheck = ''
77 rm tox.ini
78 export HYPOTHESIS_PROFILE=ci
79 '';
80
81 enabledTestPaths = [ "tests/cover" ];
82
83 # Hypothesis by default activates several "Health Checks", including one that fires if the builder is "too slow".
84 # This check is disabled [1] if Hypothesis detects a CI environment, i.e. either `CI` or `TF_BUILD` is defined [2].
85 # We set `CI=1` here using a setup hook to avoid spurious failures [3].
86 #
87 # Example error message for reference:
88 # hypothesis.errors.FailedHealthCheck: Data generation is extremely slow: Only produced 2 valid examples in 1.28 seconds (1 invalid ones and 0 exceeded maximum size). Try decreasing size of the data you're generating (with e.g. max_size or max_leaves parameters).
89 #
90 # [1]: https://github.com/HypothesisWorks/hypothesis/blob/hypothesis-python-6.130.9/hypothesis-python/src/hypothesis/_settings.py#L816-L828
91 # [2]: https://github.com/HypothesisWorks/hypothesis/blob/hypothesis-python-6.130.9/hypothesis-python/src/hypothesis/_settings.py#L756
92 # [3]: https://github.com/NixOS/nixpkgs/issues/393637
93 setupHook = ./setup-hook.sh;
94
95 disabledTests = [
96 # racy, fails to find a file sometimes
97 "test_recreate_charmap"
98 "test_uses_cached_charmap"
99 # fail when using CI profile
100 "test_given_does_not_pollute_state"
101 "test_find_does_not_pollute_state"
102 "test_does_print_on_reuse_from_database"
103 "test_prints_seed_only_on_healthcheck"
104 # calls script with the naked interpreter
105 "test_constants_from_running_file"
106 ]
107 ++ lib.optionals (pythonOlder "3.10") [
108 # not sure why these tests fail with only 3.9
109 # FileNotFoundError: [Errno 2] No such file or directory: 'git'
110 "test_observability"
111 "test_assume_has_status_reason"
112 "test_observability_captures_stateful_reprs"
113 ]
114 ++ lib.optionals (pythonAtLeast "3.12") [
115 # AssertionError: assert [b'def \... f(): pass'] == [b'def\\', b' f(): pass']
116 # https://github.com/HypothesisWorks/hypothesis/issues/4355
117 "test_clean_source"
118 ]
119 ++ lib.optionals (pythonAtLeast "3.14") [
120 "test_attrs_inference_builds"
121 "test_bound_missing_dot_access_forward_ref"
122 "test_bound_missing_forward_ref"
123 "test_bound_type_checking_only_forward_ref_wrong_type"
124 "test_bound_type_cheking_only_forward_ref"
125 "test_builds_suggests_from_type"
126 "test_bytestring_not_treated_as_generic_sequence"
127 "test_evil_prng_registration_nonsense"
128 "test_issue_4194_regression"
129 "test_passing_referenced_instance_within_function_scope_warns"
130 "test_registering_a_Random_is_idempotent"
131 "test_register_random_within_nested_function_scope"
132 "test_resolve_fwd_refs"
133 "test_resolves_forwardrefs_to_builtin_types"
134 "test_resolving_standard_collection_as_generic"
135 "test_resolving_standard_container_as_generic"
136 "test_resolving_standard_contextmanager_as_generic"
137 "test_resolving_standard_iterable_as_generic"
138 "test_resolving_standard_reversible_as_generic"
139 "test_resolving_standard_sequence_as_generic"
140 "test_specialised_collection_types"
141 ];
142
143 pythonImportsCheck = [ "hypothesis" ];
144
145 passthru = {
146 doc = stdenv.mkDerivation {
147 # Forge look and feel of multi-output derivation as best as we can.
148 #
149 # Using 'outputs = [ "doc" ];' breaks a lot of assumptions.
150 name = "${pname}-${version}-doc";
151 inherit src pname version;
152
153 postInstallSphinx = ''
154 mv $out/share/doc/* $out/share/doc/python$pythonVersion-$pname-$version
155 '';
156
157 nativeBuildInputs = [
158 sphinxHook
159 sphinx-rtd-theme
160 sphinx-hoverxref
161 sphinx-codeautolink
162 ];
163
164 inherit (python) pythonVersion;
165 inherit meta;
166 };
167 };
168
169 meta = {
170 description = "Library for property based testing";
171 mainProgram = "hypothesis";
172 homepage = "https://github.com/HypothesisWorks/hypothesis";
173 changelog = "https://hypothesis.readthedocs.io/en/latest/changes.html#v${
174 lib.replaceStrings [ "." ] [ "-" ] version
175 }";
176 license = lib.licenses.mpl20;
177 maintainers = [
178 lib.maintainers.fliegendewurst
179 ];
180 };
181}