Skip to content

Commit b5c1fd9

Browse files
authored
Add zoneinfo from Python 3.12.6 (#5400)
1 parent 23f7cbf commit b5c1fd9

File tree

15 files changed

+4363
-0
lines changed

15 files changed

+4363
-0
lines changed
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
from enum import Enum
2+
import functools
3+
import unittest
4+
5+
__all__ = [
6+
"given",
7+
"example",
8+
"assume",
9+
"reject",
10+
"register_random",
11+
"strategies",
12+
"HealthCheck",
13+
"settings",
14+
"Verbosity",
15+
]
16+
17+
from . import strategies
18+
19+
20+
def given(*_args, **_kwargs):
21+
def decorator(f):
22+
if examples := getattr(f, "_examples", []):
23+
24+
@functools.wraps(f)
25+
def test_function(self):
26+
for example_args, example_kwargs in examples:
27+
with self.subTest(*example_args, **example_kwargs):
28+
f(self, *example_args, **example_kwargs)
29+
30+
else:
31+
# If we have found no examples, we must skip the test. If @example
32+
# is applied after @given, it will re-wrap the test to remove the
33+
# skip decorator.
34+
test_function = unittest.skip(
35+
"Hypothesis required for property test with no " +
36+
"specified examples"
37+
)(f)
38+
39+
test_function._given = True
40+
return test_function
41+
42+
return decorator
43+
44+
45+
def example(*args, **kwargs):
46+
if bool(args) == bool(kwargs):
47+
raise ValueError("Must specify exactly one of *args or **kwargs")
48+
49+
def decorator(f):
50+
base_func = getattr(f, "__wrapped__", f)
51+
if not hasattr(base_func, "_examples"):
52+
base_func._examples = []
53+
54+
base_func._examples.append((args, kwargs))
55+
56+
if getattr(f, "_given", False):
57+
# If the given decorator is below all the example decorators,
58+
# it would be erroneously skipped, so we need to re-wrap the new
59+
# base function.
60+
f = given()(base_func)
61+
62+
return f
63+
64+
return decorator
65+
66+
67+
def assume(condition):
68+
if not condition:
69+
raise unittest.SkipTest("Unsatisfied assumption")
70+
return True
71+
72+
73+
def reject():
74+
assume(False)
75+
76+
77+
def register_random(*args, **kwargs):
78+
pass # pragma: no cover
79+
80+
81+
def settings(*args, **kwargs):
82+
return lambda f: f # pragma: nocover
83+
84+
85+
class HealthCheck(Enum):
86+
data_too_large = 1
87+
filter_too_much = 2
88+
too_slow = 3
89+
return_value = 5
90+
large_base_example = 7
91+
not_a_test_method = 8
92+
93+
@classmethod
94+
def all(cls):
95+
return list(cls)
96+
97+
98+
class Verbosity(Enum):
99+
quiet = 0
100+
normal = 1
101+
verbose = 2
102+
debug = 3
103+
104+
105+
class Phase(Enum):
106+
explicit = 0
107+
reuse = 1
108+
generate = 2
109+
target = 3
110+
shrink = 4
111+
explain = 5
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Stub out only the subset of the interface that we actually use in our tests.
2+
class StubClass:
3+
def __init__(self, *args, **kwargs):
4+
self.__stub_args = args
5+
self.__stub_kwargs = kwargs
6+
self.__repr = None
7+
8+
def _with_repr(self, new_repr):
9+
new_obj = self.__class__(*self.__stub_args, **self.__stub_kwargs)
10+
new_obj.__repr = new_repr
11+
return new_obj
12+
13+
def __repr__(self):
14+
if self.__repr is not None:
15+
return self.__repr
16+
17+
argstr = ", ".join(self.__stub_args)
18+
kwargstr = ", ".join(f"{kw}={val}" for kw, val in self.__stub_kwargs.items())
19+
20+
in_parens = argstr
21+
if kwargstr:
22+
in_parens += ", " + kwargstr
23+
24+
return f"{self.__class__.__qualname__}({in_parens})"
25+
26+
27+
def stub_factory(klass, name, *, with_repr=None, _seen={}):
28+
if (klass, name) not in _seen:
29+
30+
class Stub(klass):
31+
def __init__(self, *args, **kwargs):
32+
super().__init__()
33+
self.__stub_args = args
34+
self.__stub_kwargs = kwargs
35+
36+
Stub.__name__ = name
37+
Stub.__qualname__ = name
38+
if with_repr is not None:
39+
Stub._repr = None
40+
41+
_seen.setdefault((klass, name, with_repr), Stub)
42+
43+
return _seen[(klass, name, with_repr)]
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import functools
2+
3+
from ._helpers import StubClass, stub_factory
4+
5+
6+
class StubStrategy(StubClass):
7+
def __make_trailing_repr(self, transformation_name, func):
8+
func_name = func.__name__ or repr(func)
9+
return f"{self!r}.{transformation_name}({func_name})"
10+
11+
def map(self, pack):
12+
return self._with_repr(self.__make_trailing_repr("map", pack))
13+
14+
def flatmap(self, expand):
15+
return self._with_repr(self.__make_trailing_repr("flatmap", expand))
16+
17+
def filter(self, condition):
18+
return self._with_repr(self.__make_trailing_repr("filter", condition))
19+
20+
def __or__(self, other):
21+
new_repr = f"one_of({self!r}, {other!r})"
22+
return self._with_repr(new_repr)
23+
24+
25+
_STRATEGIES = {
26+
"binary",
27+
"booleans",
28+
"builds",
29+
"characters",
30+
"complex_numbers",
31+
"composite",
32+
"data",
33+
"dates",
34+
"datetimes",
35+
"decimals",
36+
"deferred",
37+
"dictionaries",
38+
"emails",
39+
"fixed_dictionaries",
40+
"floats",
41+
"fractions",
42+
"from_regex",
43+
"from_type",
44+
"frozensets",
45+
"functions",
46+
"integers",
47+
"iterables",
48+
"just",
49+
"lists",
50+
"none",
51+
"nothing",
52+
"one_of",
53+
"permutations",
54+
"random_module",
55+
"randoms",
56+
"recursive",
57+
"register_type_strategy",
58+
"runner",
59+
"sampled_from",
60+
"sets",
61+
"shared",
62+
"slices",
63+
"timedeltas",
64+
"times",
65+
"text",
66+
"tuples",
67+
"uuids",
68+
}
69+
70+
__all__ = sorted(_STRATEGIES)
71+
72+
73+
def composite(f):
74+
strategy = stub_factory(StubStrategy, f.__name__)
75+
76+
@functools.wraps(f)
77+
def inner(*args, **kwargs):
78+
return strategy(*args, **kwargs)
79+
80+
return inner
81+
82+
83+
def __getattr__(name):
84+
if name not in _STRATEGIES:
85+
raise AttributeError(f"Unknown attribute {name}")
86+
87+
return stub_factory(StubStrategy, f"hypothesis.strategies.{name}")
88+
89+
90+
def __dir__():
91+
return __all__

Lib/test/support/hypothesis_helper.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import os
2+
3+
try:
4+
import hypothesis
5+
except ImportError:
6+
from . import _hypothesis_stubs as hypothesis
7+
else:
8+
# Regrtest changes to use a tempdir as the working directory, so we have
9+
# to tell Hypothesis to use the original in order to persist the database.
10+
from .os_helper import SAVEDCWD
11+
from hypothesis.configuration import set_hypothesis_home_dir
12+
13+
set_hypothesis_home_dir(os.path.join(SAVEDCWD, ".hypothesis"))
14+
15+
# When using the real Hypothesis, we'll configure it to ignore occasional
16+
# slow tests (avoiding flakiness from random VM slowness in CI).
17+
hypothesis.settings.register_profile(
18+
"slow-is-ok",
19+
deadline=None,
20+
suppress_health_check=[
21+
hypothesis.HealthCheck.too_slow,
22+
hypothesis.HealthCheck.differing_executors,
23+
],
24+
)
25+
hypothesis.settings.load_profile("slow-is-ok")
26+
27+
# For local development, we'll write to the default on-local-disk database
28+
# of failing examples, and also use a pull-through cache to automatically
29+
# replay any failing examples discovered in CI. For details on how this
30+
# works, see https://hypothesis.readthedocs.io/en/latest/database.html
31+
if "CI" not in os.environ:
32+
from hypothesis.database import (
33+
GitHubArtifactDatabase,
34+
MultiplexedDatabase,
35+
ReadOnlyDatabase,
36+
)
37+
38+
hypothesis.settings.register_profile(
39+
"cpython-local-dev",
40+
database=MultiplexedDatabase(
41+
hypothesis.settings.default.database,
42+
ReadOnlyDatabase(GitHubArtifactDatabase("python", "cpython")),
43+
),
44+
)
45+
hypothesis.settings.load_profile("cpython-local-dev")

Lib/test/test_zoneinfo/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import os
2+
from test.support import load_package_tests
3+
4+
def load_tests(*args):
5+
return load_package_tests(os.path.dirname(__file__), *args)

Lib/test/test_zoneinfo/__main__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import unittest
2+
3+
unittest.main('test.test_zoneinfo')

0 commit comments

Comments
 (0)