Skip to content
Closed
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions Lib/test/datetimetester.py
Original file line number Diff line number Diff line change
Expand Up @@ -1909,9 +1909,6 @@ def test_fromisocalendar_value_errors(self):
(10000, 1, 1),
(0, 1, 1),
(9999999, 1, 1),
(2<<32, 1, 1),
(2019, 2<<32, 1),
(2019, 1, 2<<32),
Comment on lines -1912 to -1914

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are these removed?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mentioned this in a PR comment:

Re: datetime.date.fromisocalendar
Note that this now can throw OverflowError instead of ValueError when
using the C version of datetime. There is precedent for this, e.g.,
datetime.date.fromordinal will throw an OverflowError with the C module
but ValueError with the Python.
Also, prior to this PR, the C version fromisocalendar would throw a ValueError while the Python version throws TypeError for issues with e.g, missing arguments.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, thanks, I didn't make the connection! I'll take another look at that.

]

for isocal in isocals:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Convert :class:`datetime.date` class methods to use Argument Clinic.
Comment thread
hauntsaninja marked this conversation as resolved.
Outdated
143 changes: 76 additions & 67 deletions Modules/_datetimemodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -2876,13 +2876,18 @@ date_fromtimestamp(PyObject *cls, PyObject *obj)
cls);
}

/* Return new date from current time.
* We say this is equivalent to fromtimestamp(time.time()), and the
* only way to be sure of that is to *call* time.time(). That's not
* generally the same as calling C's time.
*/
/*[clinic input]
@classmethod
datetime.date.today

Return new date from current time.
Comment thread
hauntsaninja marked this conversation as resolved.
Outdated

Same as self.__class__.fromtimestamp(time.time())
[clinic start generated code]*/

static PyObject *
date_today(PyObject *cls, PyObject *dummy)
datetime_date_today_impl(PyTypeObject *type)
/*[clinic end generated code: output=d5474697df6b251c input=22b09b5b306fdccb]*/
{
PyObject *time;
PyObject *result;
Expand All @@ -2897,8 +2902,12 @@ date_today(PyObject *cls, PyObject *dummy)
* datetime.fromtimestamp. That's why we need all the accuracy
* time.time() delivers; if someone were gonzo about optimization,
* date.today() could get away with plain C time().
* Besides, we claim that this method is equivalent to
* fromtimestamp(time.time()), and the only way to be of that is to *call*
* time.time(). That's not generally the same as calling C's time.
*/
result = _PyObject_CallMethodIdOneArg(cls, &PyId_fromtimestamp, time);
result = _PyObject_CallMethodIdOneArg((PyObject *) type,
&PyId_fromtimestamp, time);
Py_DECREF(time);
return result;
}
Expand Down Expand Up @@ -2940,46 +2949,62 @@ datetime_date_fromtimestamp_capi(PyObject *cls, PyObject *args)
return result;
}

/* Return new date from proleptic Gregorian ordinal. Raises ValueError if
* the ordinal is out of range.
*/

/*[clinic input]
@classmethod
datetime.date.fromordinal

ordinal: int
/

int -> date corresponding to a proleptic Gregorian ordinal.

Raises ValueError if the ordinal is out of range.
[clinic start generated code]*/

static PyObject *
date_fromordinal(PyObject *cls, PyObject *args)
datetime_date_fromordinal_impl(PyTypeObject *type, int ordinal)
/*[clinic end generated code: output=ea5cc69d86614a6b input=1d7158c082e677fd]*/
{
PyObject *result = NULL;
int ordinal;

if (PyArg_ParseTuple(args, "i:fromordinal", &ordinal)) {
int year;
int month;
int day;
int year;
int month;
int day;

if (ordinal < 1)
PyErr_SetString(PyExc_ValueError, "ordinal must be "
">= 1");
else {
ord_to_ymd(ordinal, &year, &month, &day);
result = new_date_subclass_ex(year, month, day, cls);
}
if (ordinal < 1)
PyErr_SetString(PyExc_ValueError, "ordinal must be >= 1");
else {
ord_to_ymd(ordinal, &year, &month, &day);
result = new_date_subclass_ex(year, month, day, (PyObject *) type);
}
return result;
}

/* Return the new date from a string as generated by date.isoformat() */
/*[clinic input]
@classmethod
datetime.date.fromisoformat

date_string: object
Comment thread
hauntsaninja marked this conversation as resolved.
Outdated
/

str -> Construct a date from the output of date.isoformat()
[clinic start generated code]*/

static PyObject *
date_fromisoformat(PyObject *cls, PyObject *dtstr)
datetime_date_fromisoformat(PyTypeObject *type, PyObject *date_string)
/*[clinic end generated code: output=6657c70f3442350a input=cecf94602c2e7fd8]*/
{
assert(dtstr != NULL);
assert(date_string != NULL);

if (!PyUnicode_Check(dtstr)) {
if (!PyUnicode_Check(date_string)) {
PyErr_SetString(PyExc_TypeError,
"fromisoformat: argument must be str");
return NULL;
}

Py_ssize_t len;

const char *dt_ptr = PyUnicode_AsUTF8AndSize(dtstr, &len);
const char *dt_ptr = PyUnicode_AsUTF8AndSize(date_string, &len);
if (dt_ptr == NULL) {
goto invalid_string_error;
}
Expand All @@ -2998,33 +3023,32 @@ date_fromisoformat(PyObject *cls, PyObject *dtstr)
goto invalid_string_error;
}

return new_date_subclass_ex(year, month, day, cls);
return new_date_subclass_ex(year, month, day, (PyObject *) type);

invalid_string_error:
PyErr_Format(PyExc_ValueError, "Invalid isoformat string: %R", dtstr);
PyErr_Format(PyExc_ValueError, "Invalid isoformat string: %R", date_string);
return NULL;
}


static PyObject *
date_fromisocalendar(PyObject *cls, PyObject *args, PyObject *kw)
{
static char *keywords[] = {
"year", "week", "day", NULL
};
/*[clinic input]
@classmethod
datetime.date.fromisocalendar

int year, week, day;
if (PyArg_ParseTupleAndKeywords(args, kw, "iii:fromisocalendar",
keywords,
&year, &week, &day) == 0) {
if (PyErr_ExceptionMatches(PyExc_OverflowError)) {
PyErr_Format(PyExc_ValueError,
"ISO calendar component out of range");
year: int
week: int
day: int

}
Comment on lines -3020 to -3024

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now I see that before this PR we have code in fromisocalendar() to raise ValueError rather than OverflowError. Raising a ValueError seems like better behavior to me. Also, changing this would be considered backwards-incompatible, would require NEWS and What's New entries, and couldn't be backported.

I think we should find a way to retain this behavior, even if that means not using Argument Clinic for this method right now.

return NULL;
}
int, int, int -> Construct a date from the ISO year, week number and weekday.

This is the inverse of the date.isocalendar() function
Comment thread
hauntsaninja marked this conversation as resolved.
Outdated
[clinic start generated code]*/

static PyObject *
datetime_date_fromisocalendar_impl(PyTypeObject *type, int year, int week,
int day)
/*[clinic end generated code: output=7b26e15115d24df6 input=f13669bbed6c8bb6]*/
{
// Year is bounded to 0 < year < 10000 because 9999-12-31 is (9999, 52, 5)
if (year < MINYEAR || year > MAXYEAR) {
PyErr_Format(PyExc_ValueError, "Year is out of range: %d", year);
Expand Down Expand Up @@ -3062,7 +3086,7 @@ date_fromisocalendar(PyObject *cls, PyObject *args, PyObject *kw)

ord_to_ymd(day_1 + day_offset, &year, &month, &day);

return new_date_subclass_ex(year, month, day, cls);
return new_date_subclass_ex(year, month, day, (PyObject *) type);
}


Expand Down Expand Up @@ -3485,25 +3509,10 @@ static PyMethodDef date_methods[] = {

/* Class methods: */
DATETIME_DATE_FROMTIMESTAMP_METHODDEF

{"fromordinal", (PyCFunction)date_fromordinal, METH_VARARGS |
METH_CLASS,
PyDoc_STR("int -> date corresponding to a proleptic Gregorian "
"ordinal.")},

{"fromisoformat", (PyCFunction)date_fromisoformat, METH_O |
METH_CLASS,
PyDoc_STR("str -> Construct a date from the output of date.isoformat()")},

{"fromisocalendar", (PyCFunction)(void(*)(void))date_fromisocalendar,
METH_VARARGS | METH_KEYWORDS | METH_CLASS,
PyDoc_STR("int, int, int -> Construct a date from the ISO year, week "
"number and weekday.\n\n"
"This is the inverse of the date.isocalendar() function")},

{"today", (PyCFunction)date_today, METH_NOARGS | METH_CLASS,
PyDoc_STR("Current date or datetime: same as "
"self.__class__.fromtimestamp(time.time()).")},
DATETIME_DATE_FROMORDINAL_METHODDEF
DATETIME_DATE_FROMISOFORMAT_METHODDEF
DATETIME_DATE_FROMISOCALENDAR_METHODDEF
DATETIME_DATE_TODAY_METHODDEF

/* Instance methods: */

Expand Down
109 changes: 108 additions & 1 deletion Modules/clinic/_datetimemodule.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.