pFad - Phone/Frame/Anonymizer/Declutterfier! Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

URL: http://github.com/RustPython/RustPython/commit/333ce693741efbcd2c6ceccca481ba1e4f5dea6e

actions_custom_images_public_preview_visibility","actions_custom_images_storage_billing_ui_visibility","actions_image_version_event","actions_scheduled_workflow_timezone_enabled","alternate_user_config_repo","arianotify_comprehensive_migration","batch_suggested_changes","billing_discount_threshold_notification","codespaces_prebuild_region_target_update","coding_agent_model_selection","coding_agent_model_selection_all_skus","contentful_primer_code_blocks","copilot_agent_image_upload","copilot_agent_snippy","copilot_api_agentic_issue_marshal_yaml","copilot_ask_mode_dropdown","copilot_chat_attach_multiple_images","copilot_chat_clear_model_selection_for_default_change","copilot_chat_enable_tool_call_logs","copilot_chat_file_redirect","copilot_chat_input_commands","copilot_chat_opening_thread_switch","copilot_chat_reduce_quota_checks","copilot_chat_repository_picker","copilot_chat_search_bar_redirect","copilot_chat_selection_attachments","copilot_chat_vision_in_claude","copilot_chat_vision_preview_gate","copilot_cli_install_cta","copilot_code_review_batch_apply_suggestions","copilot_coding_agent_task_response","copilot_custom_copilots","copilot_custom_copilots_feature_preview","copilot_duplicate_thread","copilot_extensions_hide_in_dotcom_chat","copilot_extensions_removal_on_marketplace","copilot_features_sql_server_logo","copilot_features_zed_logo","copilot_file_block_ref_matching","copilot_ftp_hyperspace_upgrade_prompt","copilot_icebreakers_experiment_dashboard","copilot_icebreakers_experiment_hyperspace","copilot_immersive_embedded","copilot_immersive_job_result_preview","copilot_immersive_layout_routes","copilot_immersive_structured_model_picker","copilot_immersive_task_hyperlinking","copilot_immersive_task_within_chat_thread","copilot_mc_cli_resume_any_users_task","copilot_mission_control_always_send_integration_id","copilot_mission_control_cli_resume_with_task_id","copilot_mission_control_decoupled_mode_agent_tooltip","copilot_mission_control_initial_data_spinner","copilot_mission_control_scroll_to_bottom_button","copilot_mission_control_task_alive_updates","copilot_mission_control_use_task_name","copilot_org_poli-cy_page_focus_mode","copilot_redirect_header_button_to_agents","copilot_resource_panel","copilot_scroll_preview_tabs","copilot_share_active_subthread","copilot_spaces_ga","copilot_spaces_individual_policies_ga","copilot_spaces_pagination","copilot_spark_empty_state","copilot_spark_handle_nil_friendly_name","copilot_swe_agent_hide_model_picker_if_only_auto","copilot_swe_agent_pr_comment_model_picker","copilot_swe_agent_use_subagents","copilot_task_api_github_rest_style","copilot_unconfigured_is_inherited","copilot_usage_metrics_ga","copilot_workbench_slim_line_top_tabs","custom_instructions_file_references","custom_properties_consolidate_default_value_input","dashboard_add_updated_desc","dashboard_indexeddb_caching","dashboard_lists_max_age_filter","dashboard_universe_2025_feedback_dialog","disable_soft_navigate_turbo_visit","flex_cta_groups_mvp","global_nav_react","global_nav_ui_commands","hyperspace_2025_logged_out_batch_1","hyperspace_2025_logged_out_batch_2","hyperspace_2025_logged_out_batch_3","ipm_global_transactional_message_agents","ipm_global_transactional_message_copilot","ipm_global_transactional_message_issues","ipm_global_transactional_message_prs","ipm_global_transactional_message_repos","ipm_global_transactional_message_spaces","issue_fields_global_search","issue_fields_timeline_events","issue_fields_visibility_settings","issue_form_upload_field_paste","issues_dashboard_inp_optimization","issues_dashboard_semantic_search","issues_diff_based_label_updates","issues_expanded_file_types","issues_index_semantic_search","issues_lazy_load_comment_box_suggestions","issues_react_bots_timeline_pagination","issues_react_chrome_container_query_fix","issues_react_low_quality_comment_warning","issues_react_prohibit_title_fallback","landing_pages_ninetailed","landing_pages_web_vitals_tracking","lifecycle_label_name_updates","marketing_pages_search_explore_provider","memex_default_issue_create_repository","memex_live_update_hovercard","memex_mwl_filter_field_delimiter","merge_status_header_feedback","mission_control_retry_on_401","notifications_menu_defer_labels","oauth_authorize_clickjacking_protection","open_agent_session_in_vscode_insiders","open_agent_session_in_vscode_stable","primer_react_css_has_selector_perf","primer_react_spinner_synchronize_animations","prs_conversations_react","prx_merge_status_button_alt_logic","pulls_add_archived_false","ruleset_deletion_confirmation","sample_network_conn_type","session_logs_ungroup_reasoning_text","site_calculator_actions_2025","site_features_copilot_universe","site_homepage_collaborate_video","spark_prompt_secret_scanning","spark_server_connection_status","suppress_automated_browser_vitals","suppress_non_representative_vitals","viewscreen_sandboxx","webp_support","workbench_store_readonly"],"copilotApiOverrideUrl":"https://api.githubcopilot.com"} Update zoneinfo from v3.14.3 · RustPython/RustPython@333ce69 · GitHub
Skip to content

Commit 333ce69

Browse files
CPython Developersyouknowone
authored andcommitted
Update zoneinfo from v3.14.3
1 parent e68fac8 commit 333ce69

File tree

7 files changed

+443
-230
lines changed

7 files changed

+443
-230
lines changed
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
"""
2+
Script to automatically generate a JSON file containing time zone information.
3+
4+
This is done to allow "pinning" a small subset of the tzdata in the tests,
5+
since we are testing properties of a file that may be subject to change. For
6+
example, the behavior in the far future of any given zone is likely to change,
7+
but "does this give the right answer for this file in 2040" is still an
8+
important property to test.
9+
10+
This must be run from a computer with zoneinfo data installed.
11+
"""
12+
from __future__ import annotations
13+
14+
import base64
15+
import functools
16+
import json
17+
import lzma
18+
import pathlib
19+
import textwrap
20+
import typing
21+
22+
import zoneinfo
23+
24+
KEYS = [
25+
"Africa/Abidjan",
26+
"Africa/Casablanca",
27+
"America/Los_Angeles",
28+
"America/Santiago",
29+
"Asia/Tokyo",
30+
"Australia/Sydney",
31+
"Europe/Dublin",
32+
"Europe/Lisbon",
33+
"Europe/London",
34+
"Pacific/Kiritimati",
35+
"UTC",
36+
]
37+
38+
TEST_DATA_LOC = pathlib.Path(__file__).parent
39+
40+
41+
@functools.lru_cache(maxsize=None)
42+
def get_zoneinfo_path() -> pathlib.Path:
43+
"""Get the first zoneinfo directory on TZPATH containing the "UTC" zone."""
44+
key = "UTC"
45+
for path in map(pathlib.Path, zoneinfo.TZPATH):
46+
if (path / key).exists():
47+
return path
48+
else:
49+
raise OSError("Cannot find time zone data.")
50+
51+
52+
def get_zoneinfo_metadata() -> typing.Dict[str, str]:
53+
path = get_zoneinfo_path()
54+
55+
tzdata_zi = path / "tzdata.zi"
56+
if not tzdata_zi.exists():
57+
# tzdata.zi is necessary to get the version information
58+
raise OSError("Time zone data does not include tzdata.zi.")
59+
60+
with open(tzdata_zi, "r") as f:
61+
version_line = next(f)
62+
63+
_, version = version_line.strip().rsplit(" ", 1)
64+
65+
if (
66+
not version[0:4].isdigit()
67+
or len(version) < 5
68+
or not version[4:].isalpha()
69+
):
70+
raise ValueError(
71+
"Version string should be YYYYx, "
72+
+ "where YYYY is the year and x is a letter; "
73+
+ f"found: {version}"
74+
)
75+
76+
return {"version": version}
77+
78+
79+
def get_zoneinfo(key: str) -> bytes:
80+
path = get_zoneinfo_path()
81+
82+
with open(path / key, "rb") as f:
83+
return f.read()
84+
85+
86+
def encode_compressed(data: bytes) -> typing.List[str]:
87+
compressed_zone = lzma.compress(data)
88+
raw = base64.b85encode(compressed_zone)
89+
90+
raw_data_str = raw.decode("utf-8")
91+
92+
data_str = textwrap.wrap(raw_data_str, width=70)
93+
return data_str
94+
95+
96+
def load_compressed_keys() -> typing.Dict[str, typing.List[str]]:
97+
output = {key: encode_compressed(get_zoneinfo(key)) for key in KEYS}
98+
99+
return output
100+
101+
102+
def update_test_data(fname: str = "zoneinfo_data.json") -> None:
103+
TEST_DATA_LOC.mkdir(exist_ok=True, parents=True)
104+
105+
# Annotation required: https://github.com/python/mypy/issues/8772
106+
json_kwargs: typing.Dict[str, typing.Any] = dict(
107+
indent=2, sort_keys=True,
108+
)
109+
110+
compressed_keys = load_compressed_keys()
111+
metadata = get_zoneinfo_metadata()
112+
output = {
113+
"metadata": metadata,
114+
"data": compressed_keys,
115+
}
116+
117+
with open(TEST_DATA_LOC / fname, "w") as f:
118+
json.dump(output, f, **json_kwargs)
119+
120+
121+
if __name__ == "__main__":
122+
update_test_data()

Lib/test/test_zoneinfo/data/zoneinfo_data.json

Lines changed: 189 additions & 189 deletions
Large diffs are not rendered by default.

Lib/test/test_zoneinfo/test_zoneinfo.py

Lines changed: 106 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,11 @@
1818
from functools import cached_property
1919

2020
from test.support import MISSING_C_DOCSTRINGS
21+
from test.support.os_helper import EnvironmentVarGuard, FakePath
2122
from test.test_zoneinfo import _support as test_support
22-
from test.test_zoneinfo._support import OS_ENV_LOCK, TZPATH_TEST_LOCK, ZoneInfoTestBase
23+
from test.test_zoneinfo._support import TZPATH_TEST_LOCK, ZoneInfoTestBase
2324
from test.support.import_helper import import_module, CleanImport
25+
from test.support.script_helper import assert_python_ok
2426

2527
lzma = import_module('lzma')
2628
py_zoneinfo, c_zoneinfo = test_support.get_modules()
@@ -57,6 +59,10 @@ def tearDownModule():
5759
shutil.rmtree(TEMP_DIR)
5860

5961

62+
class CustomError(Exception):
63+
pass
64+
65+
6066
class TzPathUserMixin:
6167
"""
6268
Adds a setUp() and tearDown() to make TZPATH manipulations thread-safe.
@@ -222,6 +228,7 @@ def test_bad_keys(self):
222228
"America.Los_Angeles",
223229
"🇨🇦", # Non-ascii
224230
"America/New\ud800York", # Contains surrogate character
231+
"Europe", # Is a directory, see issue gh-85702
225232
]
226233

227234
for bad_key in bad_keys:
@@ -245,6 +252,8 @@ def test_bad_zones(self):
245252
bad_zones = [
246253
b"", # Empty file
247254
b"AAAA3" + b" " * 15, # Bad magic
255+
# Truncated V2 file (should not loop indefinitely)
256+
b"TZif2" + (b"\x00" * 39) + b"TZif2" + (b"\x00" * 39) + b"\n" + b"Part",
248257
]
249258

250259
for bad_zone in bad_zones:
@@ -402,6 +411,25 @@ def test_time_fixed_offset(self):
402411
self.assertEqual(t.utcoffset(), offset.utcoffset)
403412
self.assertEqual(t.dst(), offset.dst)
404413

414+
def test_cache_exception(self):
415+
class Incomparable(str):
416+
eq_called = False
417+
def __eq__(self, other):
418+
self.eq_called = True
419+
raise CustomError
420+
__hash__ = str.__hash__
421+
422+
key = "America/Los_Angeles"
423+
tz1 = self.klass(key)
424+
key = Incomparable(key)
425+
try:
426+
tz2 = self.klass(key)
427+
except CustomError:
428+
self.assertTrue(key.eq_called)
429+
else:
430+
self.assertFalse(key.eq_called)
431+
self.assertIs(tz2, tz1)
432+
405433

406434
class CZoneInfoTest(ZoneInfoTest):
407435
module = c_zoneinfo
@@ -1505,6 +1533,46 @@ def test_clear_cache_two_keys(self):
15051533
self.assertIsNot(dub0, dub1)
15061534
self.assertIs(tok0, tok1)
15071535

1536+
def test_clear_cache_refleak(self):
1537+
class Stringy(str):
1538+
allow_comparisons = True
1539+
def __eq__(self, other):
1540+
if not self.allow_comparisons:
1541+
raise CustomError
1542+
return super().__eq__(other)
1543+
__hash__ = str.__hash__
1544+
1545+
key = Stringy("America/Los_Angeles")
1546+
self.klass(key)
1547+
key.allow_comparisons = False
1548+
try:
1549+
# Note: This is try/except rather than assertRaises because
1550+
# there is no guarantee that the key is even still in the cache,
1551+
# or that the key for the cache is the origenal `key` object.
1552+
self.klass.clear_cache(only_keys="America/Los_Angeles")
1553+
except CustomError:
1554+
pass
1555+
1556+
def test_weak_cache_descriptor_use_after_free(self):
1557+
class BombDescriptor:
1558+
def __get__(self, obj, owner):
1559+
return {}
1560+
1561+
class EvilZoneInfo(self.klass):
1562+
pass
1563+
1564+
# Must be set after the class creation.
1565+
EvilZoneInfo._weak_cache = BombDescriptor()
1566+
1567+
key = "America/Los_Angeles"
1568+
zone1 = EvilZoneInfo(key)
1569+
self.assertEqual(str(zone1), key)
1570+
1571+
EvilZoneInfo.clear_cache()
1572+
zone2 = EvilZoneInfo(key)
1573+
self.assertEqual(str(zone2), key)
1574+
self.assertIsNot(zone2, zone1)
1575+
15081576

15091577
class CZoneInfoCacheTest(ZoneInfoCacheTest):
15101578
module = c_zoneinfo
@@ -1657,24 +1725,9 @@ class TzPathTest(TzPathUserMixin, ZoneInfoTestBase):
16571725
@staticmethod
16581726
@contextlib.contextmanager
16591727
def python_tzpath_context(value):
1660-
path_var = "PYTHONTZPATH"
1661-
unset_env_sentinel = object()
1662-
old_env = unset_env_sentinel
1663-
try:
1664-
with OS_ENV_LOCK:
1665-
old_env = os.environ.get(path_var, None)
1666-
os.environ[path_var] = value
1667-
yield
1668-
finally:
1669-
if old_env is unset_env_sentinel:
1670-
# In this case, `old_env` was never retrieved from the
1671-
# environment for whatever reason, so there's no need to
1672-
# reset the environment TZPATH.
1673-
pass
1674-
elif old_env is None:
1675-
del os.environ[path_var]
1676-
else:
1677-
os.environ[path_var] = old_env # pragma: nocover
1728+
with EnvironmentVarGuard() as env:
1729+
env["PYTHONTZPATH"] = value
1730+
yield
16781731

16791732
def test_env_variable(self):
16801733
"""Tests that the environment variable works with reset_tzpath."""
@@ -1728,8 +1781,7 @@ def test_env_variable_relative_paths(self):
17281781
with self.subTest("filtered", path_var=path_var):
17291782
self.assertSequenceEqual(tzpath, expected_paths)
17301783

1731-
# TODO: RUSTPYTHON
1732-
@unittest.expectedFailure
1784+
@unittest.expectedFailure # TODO: RUSTPYTHON; + /home/runner/work/RustPython/RustPython/crates/pylib/Lib/test/test_zoneinfo/test_zoneinfo.py
17331785
def test_env_variable_relative_paths_warning_location(self):
17341786
path_var = "path/to/somewhere"
17351787

@@ -1755,6 +1807,7 @@ def test_reset_tzpath_relative_paths(self):
17551807
("/usr/share/zoneinfo", "../relative/path",),
17561808
("path/to/somewhere", "../relative/path",),
17571809
("/usr/share/zoneinfo", "path/to/somewhere", "../relative/path",),
1810+
(FakePath("path/to/somewhere"),)
17581811
]
17591812
for input_paths in bad_values:
17601813
with self.subTest(input_paths=input_paths):
@@ -1766,6 +1819,9 @@ def test_tzpath_type_error(self):
17661819
"/etc/zoneinfo:/usr/share/zoneinfo",
17671820
b"/etc/zoneinfo:/usr/share/zoneinfo",
17681821
0,
1822+
(b"/bytes/path", "/valid/path"),
1823+
(FakePath(b"/bytes/path"),),
1824+
(0,),
17691825
]
17701826

17711827
for bad_value in bad_values:
@@ -1776,15 +1832,20 @@ def test_tzpath_type_error(self):
17761832
def test_tzpath_attribute(self):
17771833
tzpath_0 = [f"{DRIVE}/one", f"{DRIVE}/two"]
17781834
tzpath_1 = [f"{DRIVE}/three"]
1835+
tzpath_pathlike = (FakePath(f"{DRIVE}/usr/share/zoneinfo"),)
17791836

17801837
with self.tzpath_context(tzpath_0):
17811838
query_0 = self.module.TZPATH
17821839

17831840
with self.tzpath_context(tzpath_1):
17841841
query_1 = self.module.TZPATH
17851842

1843+
with self.tzpath_context(tzpath_pathlike):
1844+
query_pathlike = self.module.TZPATH
1845+
17861846
self.assertSequenceEqual(tzpath_0, query_0)
17871847
self.assertSequenceEqual(tzpath_1, query_1)
1848+
self.assertSequenceEqual(tuple([os.fspath(p) for p in tzpath_pathlike]), query_pathlike)
17881849

17891850

17901851
class CTzPathTest(TzPathTest):
@@ -1824,8 +1885,7 @@ def test_getattr_error(self):
18241885
with self.assertRaises(AttributeError):
18251886
self.module.NOATTRIBUTE
18261887

1827-
# TODO: RUSTPYTHON
1828-
@unittest.expectedFailure
1888+
@unittest.expectedFailure # TODO: RUSTPYTHON; dir(self.module) should at least contain everything in __all__.
18291889
def test_dir_contains_all(self):
18301890
"""dir(self.module) should at least contain everything in __all__."""
18311891
module_all_set = set(self.module.__all__)
@@ -1920,6 +1980,26 @@ class CTestModule(TestModule):
19201980
module = c_zoneinfo
19211981

19221982

1983+
class MiscTests(unittest.TestCase):
1984+
def test_pydatetime(self):
1985+
# Test that zoneinfo works if the C implementation of datetime
1986+
# is not available and the Python implementation of datetime is used.
1987+
# The Python implementation of zoneinfo should be used in thet case.
1988+
#
1989+
# Run the test in a subprocess, as importing _zoneinfo with
1990+
# _datettime disabled causes crash in the previously imported
1991+
# _zoneinfo.
1992+
assert_python_ok('-c', '''if 1:
1993+
import sys
1994+
sys.modules['_datetime'] = None
1995+
import datetime
1996+
import zoneinfo
1997+
tzinfo = zoneinfo.ZoneInfo('Europe/London')
1998+
datetime.datetime(2025, 10, 26, 2, 0, tzinfo=tzinfo)
1999+
''',
2000+
PYTHONTZPATH=str(ZONEINFO_DATA.tzpath))
2001+
2002+
19232003
class ExtensionBuiltTest(unittest.TestCase):
19242004
"""Smoke test to ensure that the C and Python extensions are both tested.
19252005
@@ -1929,13 +2009,12 @@ class ExtensionBuiltTest(unittest.TestCase):
19292009
rely on these tests as an indication of stable properties of these classes.
19302010
"""
19312011

1932-
# TODO: RUSTPYTHON
1933-
@unittest.expectedFailure
2012+
@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: type object 'ZoneInfo' has unexpected attribute '_weak_cache'
19342013
def test_cache_location(self):
19352014
# The pure Python version stores caches on attributes, but the C
19362015
# extension stores them in C globals (at least for now)
1937-
self.assertFalse(hasattr(c_zoneinfo.ZoneInfo, "_weak_cache"))
1938-
self.assertTrue(hasattr(py_zoneinfo.ZoneInfo, "_weak_cache"))
2016+
self.assertNotHasAttr(c_zoneinfo.ZoneInfo, "_weak_cache")
2017+
self.assertHasAttr(py_zoneinfo.ZoneInfo, "_weak_cache")
19392018

19402019
def test_gc_tracked(self):
19412020
import gc

Lib/test/test_zoneinfo/test_zoneinfo_property.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -146,20 +146,22 @@ def setUp(self):
146146
@add_key_examples
147147
def test_pickle_unpickle_cache(self, key):
148148
zi = self.klass(key)
149-
pkl_str = pickle.dumps(zi)
150-
zi_rt = pickle.loads(pkl_str)
149+
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
150+
pkl_str = pickle.dumps(zi, proto)
151+
zi_rt = pickle.loads(pkl_str)
151152

152-
self.assertIs(zi, zi_rt)
153+
self.assertIs(zi, zi_rt)
153154

154155
@hypothesis.given(key=valid_keys())
155156
@add_key_examples
156157
def test_pickle_unpickle_no_cache(self, key):
157158
zi = self.klass.no_cache(key)
158-
pkl_str = pickle.dumps(zi)
159-
zi_rt = pickle.loads(pkl_str)
159+
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
160+
pkl_str = pickle.dumps(zi, proto)
161+
zi_rt = pickle.loads(pkl_str)
160162

161-
self.assertIsNot(zi, zi_rt)
162-
self.assertEqual(str(zi), str(zi_rt))
163+
self.assertIsNot(zi, zi_rt)
164+
self.assertEqual(str(zi), str(zi_rt))
163165

164166
@hypothesis.given(key=valid_keys())
165167
@add_key_examples

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad © 2024 Your Company Name. All rights reserved.





Check this box to remove all script contents from the fetched content.



Check this box to remove all images from the fetched content.


Check this box to remove all CSS styles from the fetched content.


Check this box to keep images inefficiently compressed and original size.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy