"Z": 17,
"p": 18, # an additional key, only with I
"z": 19,
+ "colon_z": 24,
"G": 20,
"V": 21,
"u": 22}
raise ValueError("Cannot use '%W' or '%U' without day and year")
if "%A" not in fmt and "%a" not in fmt and "%w" not in fmt:
raise ValueError("Cannot use '%W' or '%U' without day and year")
- elif "%Z" in fmt and "%z" in fmt:
+ elif "%Z" in fmt and ("%z" in fmt or "%:z" in fmt):
raise ValueError("Cannot parse both %Z and %z")
elif "%j" in fmt and "%G" in fmt:
raise ValueError("Day of the year directive '%j' is not "
f"time data \"{val}\" doesn't match format \"{fmt}\""
)
if len(val) != found.end():
+ rest = val[found.end():]
+ # Specific check for '%:z' directive
+ if (
+ "colon_z" in found.re.groupindex
+ and found.group("colon_z") is not None
+ and rest[0] != ":"
+ ):
+ raise ValueError(
+ f"Missing colon in %:z before '{rest}', got '{val}'"
+ )
raise ValueError(
"unconverted data remains when parsing with "
- f"format \"{fmt}\": \"{val[found.end():]}\""
+ f"format \"{fmt}\": \"{rest}\""
)
else:
elif parse_code == 17:
# e.g. val='2011-12-30T00:00:00.000000UTC'; fmt='%Y-%m-%dT%H:%M:%S.%f%Z'
tz = pytz.timezone(found_dict["Z"])
- elif parse_code == 19:
+ # elif group_key in ('z', 'colon_z'):
+ elif parse_code == 19 or parse_code == 24:
# e.g. val='March 1, 2018 12:00:00+0400'; fmt='%B %d, %Y %H:%M:%S%z'
- tz = parse_timezone_directive(found_dict["z"])
+ if found_dict[group_key] is None:
+ raise ValueError(
+ f"time data \"{val}\" doesn't match format \"{fmt}\""
+ )
+
+ tz = parse_timezone_directive(found_dict[group_key])
elif parse_code == 20:
# e.g. val='2015-1-7'; fmt='%G-%V-%u'
iso_year = int(found_dict["G"])
PY311,
PY312,
PY314,
+ PY315,
PYPY,
WARNING_CHECK_DISABLED,
)
"PY311",
"PY312",
"PY314",
+ "PY315",
"PYPY",
"WARNING_CHECK_DISABLED",
]
PY311 = sys.version_info >= (3, 11)
PY312 = sys.version_info >= (3, 12)
PY314 = sys.version_info >= (3, 14)
+PY315 = sys.version_info >= (3, 15)
PYPY = platform.python_implementation() == "PyPy"
ISMUSL = "musl" in (sysconfig.get_config_var("HOST_GNU_TYPE") or "")
REF_COUNT = 2 if PY311 else 3
"PY311",
"PY312",
"PY314",
+ "PY315",
"PYPY",
]
set(self.iterparse[row_node])
)
- for event, elem in iterparse(self.path_or_buffer, events=("start", "end")):
- curr_elem = elem.tag.split("}")[1] if "}" in elem.tag else elem.tag
-
- if event == "start":
- if curr_elem == row_node:
- row = {}
-
- if row is not None:
- if self.names and iterparse_repeats:
- for col, nm in zip(self.iterparse[row_node], self.names):
- if curr_elem == col:
- elem_val = elem.text if elem.text else None
- if elem_val not in row.values() and nm not in row:
- row[nm] = elem_val
-
- if col in elem.attrib:
- if elem.attrib[col] not in row.values() and nm not in row:
- row[nm] = elem.attrib[col]
- else:
- for col in self.iterparse[row_node]:
- if curr_elem == col:
- row[col] = elem.text if elem.text else None
- if col in elem.attrib:
- row[col] = elem.attrib[col]
-
- if event == "end":
- if curr_elem == row_node and row is not None:
- dicts.append(row)
- row = None
-
- elem.clear()
- if hasattr(elem, "getprevious"):
- while (
- elem.getprevious() is not None and elem.getparent() is not None
- ):
- del elem.getparent()[0]
+ parser = iterparse(self.path_or_buffer, events=("start", "end"))
+ try:
+ for event, elem in parser:
+ curr_elem = elem.tag.split("}")[1] if "}" in elem.tag else elem.tag
+
+ if event == "start":
+ if curr_elem == row_node:
+ row = {}
+
+ if row is not None:
+ if self.names and iterparse_repeats:
+ for col, nm in zip(self.iterparse[row_node], self.names):
+ if curr_elem == col:
+ elem_val = elem.text if elem.text else None
+ if elem_val not in row.values() and nm not in row:
+ row[nm] = elem_val
+
+ if col in elem.attrib:
+ if (
+ elem.attrib[col] not in row.values()
+ and nm not in row
+ ):
+ row[nm] = elem.attrib[col]
+ else:
+ for col in self.iterparse[row_node]:
+ if curr_elem == col:
+ row[col] = elem.text if elem.text else None
+ if col in elem.attrib:
+ row[col] = elem.attrib[col]
+
+ if event == "end":
+ if curr_elem == row_node and row is not None:
+ dicts.append(row)
+ row = None
+
+ elem.clear()
+ if hasattr(elem, "getprevious"):
+ while (
+ elem.getprevious() is not None
+ and elem.getparent() is not None
+ ):
+ del elem.getparent()[0]
+ finally:
+ if hasattr(parser, "close"):
+ parser.close()
if dicts == []:
raise ParserError("No result from selected items in iterparse.")
)
import pandas.util._test_decorators as td
+from pandas.compat import PY315
from pandas.core.dtypes.common import is_integer
import pandas as pd
import pandas._testing as tm
# We pass through a TypeError raised by numpy
-_slice_msg = "slice indices must be integers or None or have an __index__ method"
+if PY315:
+ _slice_msg = "slice indices must be integers or have an __index__ method"
+else:
+ _slice_msg = "slice indices must be integers or None or have an __index__ method"
class TestDataFrameIndexing:
import numpy as np
import pytest
+from pandas.compat import PY315
+
from pandas import (
DataFrame,
PeriodIndex,
# GH#6716
idx = make_range(start="2013/01/01", freq="D", periods=400)
- msg = "slice indices must be integers or None or have an __index__ method"
+ if PY315:
+ msg = "slice indices must be integers or have an __index__ method"
+ else:
+ msg = "slice indices must be integers or None or have an __index__ method"
# slices against index should raise IndexError
values = [
"2014",
def test_range_slice_seconds(self, make_range):
# GH#6716
idx = make_range(start="2013/01/01 09:00:00", freq="s", periods=4000)
- msg = "slice indices must be integers or None or have an __index__ method"
+ if PY315:
+ msg = "slice indices must be integers or have an __index__ method"
+ else:
+ msg = "slice indices must be integers or None or have an __index__ method"
# slices against index should raise IndexError
values = [
import pytest
from pandas._libs.tslibs import Timestamp
+from pandas.compat import PY315
from pandas.core.dtypes.common import (
is_integer_dtype,
if len(index) == 0:
# 0 vs 0.5 in error message varies with numpy version
msg = "index (0|0.5) is out of bounds for axis 0 with size 0"
+ elif PY315:
+ msg = "slice indices must be integers or have an __index__ method"
else:
msg = "slice indices must be integers or None or have an __index__ method"
import numpy as np
import pytest
+from pandas.compat import PY315
+
from pandas import (
DataFrame,
Index,
# setitem
if indexer_sli is tm.iloc:
# otherwise we keep the same message as above
- msg = "slice indices must be integers or None or have an __index__ method"
+ if PY315:
+ msg = "slice indices must be integers or have an __index__ method"
+ else:
+ msg = (
+ "slice indices must be integers or None or have an __index__ method"
+ )
with pytest.raises(TypeError, match=msg):
indexer_sli(s)[idx] = 0
from pandas.compat import (
PY310,
PY314,
+ PY315,
)
from pandas.errors import OutOfBoundsDatetime
def test_constructor_keyword(self):
# GH#10758
- msg = "function missing required argument 'day'|Required argument 'day'"
+ msg = "|".join(
+ [
+ r"datetime\(\) missing required argument 'day'", # PY315
+ "function missing required argument 'day'",
+ "Required argument 'day'",
+ ]
+ )
with pytest.raises(TypeError, match=msg):
Timestamp(year=2000, month=1)
# GH#31200
# The exact error message of datetime() depends on its version
- msg1 = r"function missing required argument '(year|month|day)' \(pos [123]\)"
+ if PY315:
+ msg1 = (
+ r"datetime\(\) missing required argument "
+ r"'(year|month|day)' \(pos [123]\)"
+ )
+ else:
+ msg1 = (
+ r"function missing required argument '(year|month|day)' \(pos [123]\)"
+ )
msg2 = r"Required argument '(year|month|day)' \(pos [123]\) not found"
msg = "|".join([msg1, msg2])
iNaT,
parsing,
)
-from pandas.compat import PY314
+from pandas.compat import (
+ PY314,
+ PY315,
+)
from pandas.errors import (
OutOfBoundsDatetime,
OutOfBoundsTimedelta,
expected = DatetimeIndex(expected_dates)
tm.assert_index_equal(result, expected)
+ @pytest.mark.xfail(
+ not PY315, reason="%:z directive not supported prior to 3.15", raises=ValueError
+ )
+ def test_to_datetime_colon_z_offset(self):
+ dates = [
+ "2010-01-01 12:00:00+04:00",
+ "2010-01-01 12:00:00+04:30",
+ "2010-01-01 12:00:00-05:00",
+ ]
+ expected_dates = [
+ "2010-01-01 08:00:00+00:00",
+ "2010-01-01 07:30:00+00:00",
+ "2010-01-01 17:00:00+00:00",
+ ]
+ fmt = "%Y-%m-%d %H:%M:%S%:z"
+
+ result = to_datetime(dates, format=fmt, utc=True)
+ expected = DatetimeIndex(expected_dates)
+ tm.assert_index_equal(result, expected)
+
+ def test_to_datetime_missing_colon_z_offset(self):
+ # test adapted from python/cpython#136961
+ dates = ["+04:0030"]
+ fmt = "%:z"
+
+ if PY315:
+ msg = r"Missing colon in %:z before '30', got '\+04:0030'"
+ else:
+ msg = "':' is a bad directive in format '%:z'"
+
+ with pytest.raises(ValueError, match=msg):
+ to_datetime(dates, format=fmt, utc=True)
+
@pytest.mark.parametrize(
"offset", ["+0", "-1foo", "UTCbar", ":10", "+01:000:01", ""]
)
@pytest.mark.parametrize("errors", ["coerce", "raise", "ignore"])
def test_invalid_format_raises(self, errors):
# https://github.com/pandas-dev/pandas/issues/50255
- with pytest.raises(
- ValueError, match="':' is a bad directive in format 'H%:M%:S%"
- ):
+ if PY315:
+ msg = r"':M' is a bad directive in format 'H%:M%:S%"
+ else:
+ msg = "':' is a bad directive in format 'H%:M%:S%"
+
+ with pytest.raises(ValueError, match=msg):
to_datetime(["00:00:00"], format="H%:M%:S%", errors=errors)
@pytest.mark.parametrize("value", ["a", "00:01:99"])