mirror of
https://github.com/python/cpython.git
synced 2025-10-19 07:53:46 +00:00
[3.14] gh-123681: Check NORMALIZE_CENTURY behavior at runtime; require C99 (GH-136022) (GH-137947)
A runtime check is needed to support cross-compiling.
Remove the _Py_NORMALIZE_CENTURY macro.
Remove _pydatetime.py's _can_support_c99.
(cherry picked from commit 719e5c3f71
)
Co-authored-by: Petr Viktorin <encukou@gmail.com>
Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
This commit is contained in:
parent
fad7bfc282
commit
7d9b2671c6
8 changed files with 48 additions and 191 deletions
|
@ -213,17 +213,6 @@ def _need_normalize_century():
|
|||
_normalize_century = True
|
||||
return _normalize_century
|
||||
|
||||
_supports_c99 = None
|
||||
def _can_support_c99():
|
||||
global _supports_c99
|
||||
if _supports_c99 is None:
|
||||
try:
|
||||
_supports_c99 = (
|
||||
_time.strftime("%F", (1900, 1, 1, 0, 0, 0, 0, 1, 0)) == "1900-01-01")
|
||||
except ValueError:
|
||||
_supports_c99 = False
|
||||
return _supports_c99
|
||||
|
||||
# Correctly substitute for %z and %Z escapes in strftime formats.
|
||||
def _wrap_strftime(object, format, timetuple):
|
||||
# Don't call utcoffset() or tzname() unless actually needed.
|
||||
|
@ -283,7 +272,7 @@ def _wrap_strftime(object, format, timetuple):
|
|||
newformat.append(Zreplace)
|
||||
# Note that datetime(1000, 1, 1).strftime('%G') == '1000' so
|
||||
# year 1000 for %G can go on the fast path.
|
||||
elif ((ch in 'YG' or ch in 'FC' and _can_support_c99()) and
|
||||
elif ((ch in 'YG' or ch in 'FC') and
|
||||
object.year < 1000 and _need_normalize_century()):
|
||||
if ch == 'G':
|
||||
year = int(_time.strftime("%G", timetuple))
|
||||
|
|
|
@ -1807,7 +1807,7 @@ def test_bool(self):
|
|||
self.assertTrue(self.theclass.min)
|
||||
self.assertTrue(self.theclass.max)
|
||||
|
||||
def test_strftime_y2k(self):
|
||||
def check_strftime_y2k(self, specifier):
|
||||
# Test that years less than 1000 are 0-padded; note that the beginning
|
||||
# of an ISO 8601 year may fall in an ISO week of the year before, and
|
||||
# therefore needs an offset of -1 when formatting with '%G'.
|
||||
|
@ -1821,22 +1821,28 @@ def test_strftime_y2k(self):
|
|||
(1000, 0),
|
||||
(1970, 0),
|
||||
)
|
||||
specifiers = 'YG'
|
||||
if _time.strftime('%F', (1900, 1, 1, 0, 0, 0, 0, 1, 0)) == '1900-01-01':
|
||||
specifiers += 'FC'
|
||||
for year, g_offset in dataset:
|
||||
for specifier in specifiers:
|
||||
with self.subTest(year=year, specifier=specifier):
|
||||
d = self.theclass(year, 1, 1)
|
||||
if specifier == 'G':
|
||||
year += g_offset
|
||||
if specifier == 'C':
|
||||
expected = f"{year // 100:02d}"
|
||||
else:
|
||||
expected = f"{year:04d}"
|
||||
if specifier == 'F':
|
||||
expected += f"-01-01"
|
||||
self.assertEqual(d.strftime(f"%{specifier}"), expected)
|
||||
with self.subTest(year=year, specifier=specifier):
|
||||
d = self.theclass(year, 1, 1)
|
||||
if specifier == 'G':
|
||||
year += g_offset
|
||||
if specifier == 'C':
|
||||
expected = f"{year // 100:02d}"
|
||||
else:
|
||||
expected = f"{year:04d}"
|
||||
if specifier == 'F':
|
||||
expected += f"-01-01"
|
||||
self.assertEqual(d.strftime(f"%{specifier}"), expected)
|
||||
|
||||
def test_strftime_y2k(self):
|
||||
self.check_strftime_y2k('Y')
|
||||
self.check_strftime_y2k('G')
|
||||
|
||||
def test_strftime_y2k_c99(self):
|
||||
# CPython requires C11; specifiers new in C99 must work.
|
||||
# (Other implementations may want to disable this test.)
|
||||
self.check_strftime_y2k('F')
|
||||
self.check_strftime_y2k('C')
|
||||
|
||||
def test_replace(self):
|
||||
cls = self.theclass
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
Check the ``strftime()`` behavior at runtime instead of at the compile time
|
||||
to support cross-compiling.
|
||||
Remove the internal macro ``_Py_NORMALIZE_CENTURY``.
|
|
@ -1753,6 +1753,24 @@ format_utcoffset(char *buf, size_t buflen, const char *sep,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Check whether year with century should be normalized for strftime. */
|
||||
inline static int
|
||||
normalize_century(void)
|
||||
{
|
||||
static int cache = -1;
|
||||
if (cache < 0) {
|
||||
char year[5];
|
||||
struct tm date = {
|
||||
.tm_year = -1801,
|
||||
.tm_mon = 0,
|
||||
.tm_mday = 1
|
||||
};
|
||||
cache = (strftime(year, sizeof(year), "%Y", &date) &&
|
||||
strcmp(year, "0099") != 0);
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
make_somezreplacement(PyObject *object, char *sep, PyObject *tzinfoarg)
|
||||
{
|
||||
|
@ -1924,10 +1942,9 @@ wrap_strftime(PyObject *object, PyObject *format, PyObject *timetuple,
|
|||
}
|
||||
replacement = freplacement;
|
||||
}
|
||||
#ifdef _Py_NORMALIZE_CENTURY
|
||||
else if (ch == 'Y' || ch == 'G'
|
||||
|| ch == 'F' || ch == 'C'
|
||||
) {
|
||||
else if (normalize_century()
|
||||
&& (ch == 'Y' || ch == 'G' || ch == 'F' || ch == 'C'))
|
||||
{
|
||||
/* 0-pad year with century as necessary */
|
||||
PyObject *item = PySequence_GetItem(timetuple, 0);
|
||||
if (item == NULL) {
|
||||
|
@ -1978,7 +1995,6 @@ wrap_strftime(PyObject *object, PyObject *format, PyObject *timetuple,
|
|||
}
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
/* percent followed by something else */
|
||||
continue;
|
||||
|
|
|
@ -226,6 +226,7 @@ Modules/_datetimemodule.c datetime_isoformat specs -
|
|||
Modules/_datetimemodule.c parse_hh_mm_ss_ff correction -
|
||||
Modules/_datetimemodule.c time_isoformat specs -
|
||||
Modules/_datetimemodule.c - capi_types -
|
||||
Modules/_datetimemodule.c normalize_century cache -
|
||||
Modules/_decimal/_decimal.c - cond_map_template -
|
||||
Modules/_decimal/_decimal.c - dec_signal_string -
|
||||
Modules/_decimal/_decimal.c - dflt_ctx -
|
||||
|
|
Can't render this file because it has a wrong number of fields in line 4.
|
104
configure
generated
vendored
104
configure
generated
vendored
|
@ -28186,110 +28186,6 @@ printf "%s\n" "#define HAVE_STAT_TV_NSEC2 1" >>confdefs.h
|
|||
|
||||
fi
|
||||
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether year with century should be normalized for strftime" >&5
|
||||
printf %s "checking whether year with century should be normalized for strftime... " >&6; }
|
||||
if test ${ac_cv_normalize_century+y}
|
||||
then :
|
||||
printf %s "(cached) " >&6
|
||||
else case e in #(
|
||||
e)
|
||||
if test "$cross_compiling" = yes
|
||||
then :
|
||||
ac_cv_normalize_century=yes
|
||||
else case e in #(
|
||||
e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext
|
||||
/* end confdefs.h. */
|
||||
|
||||
#include <time.h>
|
||||
#include <string.h>
|
||||
|
||||
int main(void)
|
||||
{
|
||||
char year[5];
|
||||
struct tm date = {
|
||||
.tm_year = -1801,
|
||||
.tm_mon = 0,
|
||||
.tm_mday = 1
|
||||
};
|
||||
if (strftime(year, sizeof(year), "%Y", &date) && !strcmp(year, "0099")) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
_ACEOF
|
||||
if ac_fn_c_try_run "$LINENO"
|
||||
then :
|
||||
ac_cv_normalize_century=yes
|
||||
else case e in #(
|
||||
e) ac_cv_normalize_century=no ;;
|
||||
esac
|
||||
fi
|
||||
rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
|
||||
conftest.$ac_objext conftest.beam conftest.$ac_ext ;;
|
||||
esac
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_normalize_century" >&5
|
||||
printf "%s\n" "$ac_cv_normalize_century" >&6; }
|
||||
if test "$ac_cv_normalize_century" = yes
|
||||
then
|
||||
|
||||
printf "%s\n" "#define _Py_NORMALIZE_CENTURY 1" >>confdefs.h
|
||||
|
||||
fi
|
||||
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C99-compatible strftime specifiers are supported" >&5
|
||||
printf %s "checking whether C99-compatible strftime specifiers are supported... " >&6; }
|
||||
if test ${ac_cv_strftime_c99_support+y}
|
||||
then :
|
||||
printf %s "(cached) " >&6
|
||||
else case e in #(
|
||||
e)
|
||||
if test "$cross_compiling" = yes
|
||||
then :
|
||||
ac_cv_strftime_c99_support=
|
||||
else case e in #(
|
||||
e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext
|
||||
/* end confdefs.h. */
|
||||
|
||||
#include <time.h>
|
||||
#include <string.h>
|
||||
|
||||
int main(void)
|
||||
{
|
||||
char full_date[11];
|
||||
struct tm date = {
|
||||
.tm_year = 0,
|
||||
.tm_mon = 0,
|
||||
.tm_mday = 1
|
||||
};
|
||||
if (strftime(full_date, sizeof(full_date), "%F", &date) && !strcmp(full_date, "1900-01-01")) {
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
_ACEOF
|
||||
if ac_fn_c_try_run "$LINENO"
|
||||
then :
|
||||
ac_cv_strftime_c99_support=yes
|
||||
else case e in #(
|
||||
e) as_fn_error $? "Python requires C99-compatible strftime specifiers" "$LINENO" 5 ;;
|
||||
esac
|
||||
fi
|
||||
rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
|
||||
conftest.$ac_objext conftest.beam conftest.$ac_ext ;;
|
||||
esac
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_strftime_c99_support" >&5
|
||||
printf "%s\n" "$ac_cv_strftime_c99_support" >&6; }
|
||||
|
||||
have_curses=no
|
||||
have_panel=no
|
||||
|
||||
|
|
51
configure.ac
51
configure.ac
|
@ -6792,57 +6792,6 @@ then
|
|||
[Define if you have struct stat.st_mtimensec])
|
||||
fi
|
||||
|
||||
AC_CACHE_CHECK([whether year with century should be normalized for strftime], [ac_cv_normalize_century], [
|
||||
AC_RUN_IFELSE([AC_LANG_SOURCE([[
|
||||
#include <time.h>
|
||||
#include <string.h>
|
||||
|
||||
int main(void)
|
||||
{
|
||||
char year[5];
|
||||
struct tm date = {
|
||||
.tm_year = -1801,
|
||||
.tm_mon = 0,
|
||||
.tm_mday = 1
|
||||
};
|
||||
if (strftime(year, sizeof(year), "%Y", &date) && !strcmp(year, "0099")) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
]])],
|
||||
[ac_cv_normalize_century=yes],
|
||||
[ac_cv_normalize_century=no],
|
||||
[ac_cv_normalize_century=yes])])
|
||||
if test "$ac_cv_normalize_century" = yes
|
||||
then
|
||||
AC_DEFINE([_Py_NORMALIZE_CENTURY], [1],
|
||||
[Define if year with century should be normalized for strftime.])
|
||||
fi
|
||||
|
||||
AC_CACHE_CHECK([whether C99-compatible strftime specifiers are supported], [ac_cv_strftime_c99_support], [
|
||||
AC_RUN_IFELSE([AC_LANG_SOURCE([[
|
||||
#include <time.h>
|
||||
#include <string.h>
|
||||
|
||||
int main(void)
|
||||
{
|
||||
char full_date[11];
|
||||
struct tm date = {
|
||||
.tm_year = 0,
|
||||
.tm_mon = 0,
|
||||
.tm_mday = 1
|
||||
};
|
||||
if (strftime(full_date, sizeof(full_date), "%F", &date) && !strcmp(full_date, "1900-01-01")) {
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
]])],
|
||||
[ac_cv_strftime_c99_support=yes],
|
||||
[AC_MSG_ERROR([Python requires C99-compatible strftime specifiers])],
|
||||
[ac_cv_strftime_c99_support=])])
|
||||
|
||||
dnl check for ncursesw/ncurses and panelw/panel
|
||||
dnl NOTE: old curses is not detected.
|
||||
dnl have_curses=[no, yes]
|
||||
|
|
|
@ -2023,9 +2023,6 @@
|
|||
/* HACL* library can compile SIMD256 implementations */
|
||||
#undef _Py_HACL_CAN_COMPILE_VEC256
|
||||
|
||||
/* Define if year with century should be normalized for strftime. */
|
||||
#undef _Py_NORMALIZE_CENTURY
|
||||
|
||||
/* Define to force use of thread-safe errno, h_errno, and other functions */
|
||||
#undef _REENTRANT
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue