Skip to content

Commit c7a5d16

Browse files
authored
Merge pull request #1001 from bashtage/fixes-for-pandas-3
MAINT: Fix PDF for pandas 3 changes
2 parents 0f90ea8 + 2e7ea0d commit c7a5d16

22 files changed

+110
-92
lines changed

ci/azure/azure_template_posix.yml

+12-7
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,22 @@ jobs:
1616
vmImage: ${{ parameters.vmImage }}
1717
strategy:
1818
matrix:
19-
python38_legacy:
20-
python.version: '3.8'
21-
PANDAS: 1.5.3
2219
python39_legacy:
2320
python.version: '3.9'
2421
PANDAS: 1.5.3
25-
python310_recent:
26-
python.version: '3.9'
22+
NUMPY: 1.23.4
23+
python310_legacy:
24+
python.version: '3.10'
2725
PANDAS: 2.0.3
26+
NUMPY: 1.24.4
2827
python311_latest:
2928
python.version: '3.11'
29+
PANDAS: 2.1.4
30+
NUMPY: 1.26.4
3031
python312_latest:
3132
python.version: '3.12'
33+
python313_latest:
34+
python.version: '3.13'
3235

3336
steps:
3437
- task: UsePythonVersion@0
@@ -40,6 +43,9 @@ jobs:
4043
python -m pip install pip setuptools -U
4144
python -m pip install -r requirements.txt
4245
python -m pip install -r requirements-dev.txt
46+
if [[ -n ${NUMPY} ]]; then
47+
python -m pip install numpy==${NUMPY}
48+
fi;
4349
if [[ -n ${PANDAS} ]]; then
4450
python -m pip install pandas==${PANDAS}
4551
fi;
@@ -73,9 +79,8 @@ jobs:
7379
testRunTitle: 'Python $(python.version)'
7480
condition: succeededOrFailed()
7581

76-
- task: PublishCodeCoverageResults@1
82+
- task: PublishCodeCoverageResults@2
7783
inputs:
78-
codeCoverageTool: Cobertura
7984
summaryFileLocation: '$(System.DefaultWorkingDirectory)/**/coverage.xml'
8085
condition: eq(variables['coverage'], 'true')
8186

ci/azure/azure_template_windows.yml

+13-22
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ parameters:
77
# defaults for any parameters that are not specified
88
name: ''
99
vmImage: ''
10-
coverage: true
1110

1211
jobs:
1312

@@ -16,19 +15,21 @@ jobs:
1615
vmImage: ${{ parameters.vmImage }}
1716
strategy:
1817
matrix:
19-
python38_legacy:
20-
python.version: '3.8'
21-
PANDAS: 1.5.3
2218
python39_legecy:
2319
python.version: '3.9'
2420
PANDAS: 1.5.3
21+
NUMPY: 1.23.4
2522
python310_recent:
2623
python.version: '3.10'
27-
PANDAS: 2.0.3
24+
NUMPY: 1.24.4
2825
python311_latest:
2926
python.version: '3.11'
27+
PANDAS: 2.1.4
28+
NUMPY: 1.26.4
3029
python312_latest:
3130
python.version: '3.12'
31+
python313_latest:
32+
python.version: '3.13'
3233

3334
steps:
3435
- task: UsePythonVersion@0
@@ -44,10 +45,13 @@ jobs:
4445
displayName: 'Install dependencies'
4546
4647
- powershell: |
48+
if ($null -ne $env:NUMPY) {
49+
python -m pip install numpy==$env:NUMPY
50+
}
4751
if ($null -ne $env:PANDAS) {
48-
python -m pip install pandas==${PANDAS}
52+
python -m pip install pandas==$env:PANDAS
4953
}
50-
displayName: 'Update pandas (if needed)'
54+
displayName: 'Update numpy and pandas (if needed)'
5155
5256
- script: |
5357
python -m pip install -e . -vv
@@ -64,25 +68,12 @@ jobs:
6468
6569
- script: |
6670
echo "Testing editable install"
67-
if [[ ${COVERAGE} == "true" ]]; then
68-
export COVERAGE_OPTS="--cov-config .coveragerc --cov=pandas_datareader --cov-report xml:coverage.xml --cov-report term"
69-
fi
70-
echo pytest -m "${PYTEST_PATTERN}" --junitxml=junit/test-results.xml -n auto --durations=25 ${COVERAGE_OPTS} pandas_datareader/tests
71-
pytest -m "${PYTEST_PATTERN}" --junitxml=junit/test-results.xml -n auto --durations=25 ${COVERAGE_OPTS} pandas_datareader/tests
71+
echo pytest --durations=25 pandas_datareader/tests
72+
pytest --durations=25 pandas_datareader/tests
7273
displayName: 'Run tests'
7374
7475
- task: PublishTestResults@2
7576
inputs:
7677
testResultsFiles: '**/test-results.xml'
7778
testRunTitle: 'Python $(python.version)'
7879
condition: succeededOrFailed()
79-
80-
- task: PublishCodeCoverageResults@1
81-
inputs:
82-
codeCoverageTool: Cobertura
83-
summaryFileLocation: '$(System.DefaultWorkingDirectory)/**/coverage.xml'
84-
condition: eq(variables['coverage'], 'true')
85-
86-
- bash: bash <(curl -s https://codecov.io/bash)
87-
displayName: 'CodeCov upload'
88-
condition: eq(variables['coverage'], 'true')

pandas_datareader/base.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,9 @@ def _read_url_as_StringIO(self, url, params=None):
115115
if len(text) == 0:
116116
service = self.__class__.__name__
117117
raise OSError(
118-
"{} request returned no data; check URL for invalid "
119-
"inputs: {}".format(service, self.url)
118+
"{} request returned no data; check URL for invalid inputs: {}".format(
119+
service, self.url
120+
)
120121
)
121122
if isinstance(text, bytes):
122123
out.write(text.decode("utf-8"))

pandas_datareader/compat/__init__.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from io import StringIO
2+
import sys
23
from urllib.error import HTTPError
34

45
from pandas.api.types import is_list_like, is_number
@@ -12,7 +13,7 @@
1213
"assert_frame_equal",
1314
"is_list_like",
1415
"is_number",
15-
"reduce",
16+
"PYTHON_LT_3_10",
1617
]
1718

1819

@@ -31,3 +32,6 @@ def get_filepath_or_buffer(filepath_or_buffer, encoding=None, compression=None):
3132
filepath_or_buffer, encoding=encoding, compression=None
3233
)
3334
return tmp
35+
36+
37+
PYTHON_LT_3_10 = sys.version_info <= (3, 10)

pandas_datareader/data.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -683,8 +683,7 @@ def DataReader(
683683
def Options(symbol, data_source=None, session=None):
684684
if data_source is None:
685685
warnings.warn(
686-
"Options(symbol) is deprecated, use Options(symbol,"
687-
" data_source) instead",
686+
"Options(symbol) is deprecated, use Options(symbol, data_source) instead",
688687
FutureWarning,
689688
stacklevel=2,
690689
)

pandas_datareader/famafrench.py

+16-11
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from pandas import read_csv, to_datetime
77

88
from pandas_datareader.base import _BaseReader
9-
from pandas_datareader.compat import StringIO
9+
from pandas_datareader.compat import PYTHON_LT_3_10, StringIO
1010

1111
_URL = "http://mba.tuck.dartmouth.edu/pages/faculty/ken.french/"
1212
_URL_PREFIX = "ftp/"
@@ -57,7 +57,7 @@ def _read_zipfile(self, url):
5757
with tempfile.TemporaryFile() as tmpf:
5858
tmpf.write(raw)
5959
with ZipFile(tmpf, "r") as zf:
60-
try:
60+
try:
6161
data = zf.open(zf.namelist()[0]).read().decode("utf-8", "ignore")
6262
except UnicodeDecodeError:
6363
data = zf.open(zf.namelist()[0]).read().decode(encoding="cp1252")
@@ -78,8 +78,6 @@ def read(self):
7878
def _read_one_data(self, url, params):
7979
params = {
8080
"index_col": 0,
81-
"parse_dates": [0],
82-
"date_parser": _parse_date_famafrench,
8381
}
8482

8583
# headers in these files are not valid
@@ -89,7 +87,12 @@ def _read_one_data(self, url, params):
8987
else:
9088
c = ["Count"]
9189
r = list(range(0, 105, 5))
92-
params["names"] = ["Date"] + c + list(zip(r, r[1:], strict=False))
90+
91+
if PYTHON_LT_3_10:
92+
additional_params = list(zip(r, r[1:])) # noqa: B905
93+
else:
94+
additional_params = list(zip(r, r[1:], strict=False))
95+
params["names"] = ["Date"] + c + additional_params
9396

9497
if self.symbols != "Prior_2-12_Breakpoints":
9598
params["skiprows"] = 1
@@ -111,12 +114,14 @@ def _read_one_data(self, url, params):
111114
start = 0 if not match else match.start()
112115

113116
df = read_csv(StringIO("Date" + src[start:]), **params)
114-
try:
115-
idx_name = df.index.name # hack for pandas 0.16.2
116-
df = df.to_period(df.index.inferred_freq[:1])
117-
df.index.name = idx_name
118-
except Exception:
119-
pass
117+
if df.index.min() > 190000:
118+
df.index = to_datetime(df.index.astype(str), format="%Y%m").to_period(
119+
freq="M"
120+
)
121+
else:
122+
df.index = to_datetime(df.index.astype(str), format="%Y").to_period(
123+
freq="Y"
124+
)
120125
df = df.truncate(self.start, self.end)
121126
datasets[i] = df
122127

pandas_datareader/fred.py

+6-1
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,13 @@ def fetch_data(url, name):
5858
) from exc
5959
raise
6060

61+
try:
62+
data = [fetch_data(url, n) for url, n in zip(urls, names, strict=True)]
63+
except TypeError:
64+
# Python 3.9 only
65+
data = [fetch_data(url, n) for url, n in zip(urls, names)] # noqa: B905
6166
df = concat(
62-
[fetch_data(url, n) for url, n in zip(urls, names, strict=True)],
67+
data,
6368
axis=1,
6469
join="outer",
6570
)

pandas_datareader/iex/daily.py

-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515

1616

1717
class IEXDailyReader(_DailyBaseReader):
18-
1918
"""
2019
Returns DataFrame of historical stock prices
2120
from symbols, over date range, start to end. To avoid being penalized by

pandas_datareader/moex.py

+6-4
Original file line numberDiff line numberDiff line change
@@ -197,8 +197,9 @@ def read_all_boards(self):
197197

198198
if len(dfs) == 0:
199199
raise OSError(
200-
"{} returned no data; "
201-
"check URL or correct a date".format(self.__class__.__name__)
200+
"{} returned no data; check URL or correct a date".format(
201+
self.__class__.__name__
202+
)
202203
)
203204
elif len(dfs) > 1:
204205
b = pd.concat(dfs, axis=0, join="outer", sort=True)
@@ -226,8 +227,9 @@ def _read_url_as_String(self, url, params=None):
226227
if len(text) == 0:
227228
service = self.__class__.__name__
228229
raise OSError(
229-
"{} request returned no data; check URL for invalid "
230-
"inputs: {}".format(service, self.url)
230+
"{} request returned no data; check URL for invalid inputs: {}".format(
231+
service, self.url
232+
)
231233
)
232234
if isinstance(text, bytes):
233235
text = text.decode("windows-1251")

pandas_datareader/stooq.py

-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33

44
class StooqDailyReader(_DailyBaseReader):
5-
65
"""
76
Returns DataFrame/dict of Dataframes of historical stock prices from
87
symbols, over date range, start to end.

pandas_datareader/tests/test_bankofcanada.py

+9-4
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,15 @@ def check_bankofcanada_inverted(self, code):
4141
date.today() - timedelta(days=30),
4242
date.today(),
4343
)
44-
45-
pairs = zip(
46-
(1 / df)[symbol].tolist(), df_i[symbol_inverted].tolist(), strict=True
47-
)
44+
try:
45+
pairs = zip(
46+
(1 / df)[symbol].tolist(), df_i[symbol_inverted].tolist(), strict=True
47+
)
48+
except TypeError:
49+
# Python 3.9 only
50+
pairs = zip( # noqa: B905
51+
(1 / df)[symbol].tolist(), df_i[symbol_inverted].tolist()
52+
)
4853
assert all(a - b < 0.01 for a, b in pairs)
4954

5055
def test_bankofcanada_usd_count(self):

pandas_datareader/tests/test_famafrench.py

+21-19
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,10 @@ def test_get_available_datasets(self):
3232

3333
def test_index(self):
3434
ff = web.DataReader("F-F_Research_Data_Factors", "famafrench")
35-
assert ff[0].index.freq == "M"
36-
assert ff[1].index.freq == "A-DEC"
35+
# M is for legacy pandas < 2
36+
assert ff[0].index.freq.name in ("ME", "M")
37+
# A-DEC is for legacy pandas < 2
38+
assert ff[1].index.freq.name in ("YE-DEC", "A-DEC")
3739

3840
def test_f_f_research(self):
3941
results = web.DataReader(
@@ -49,49 +51,49 @@ def test_f_f_research(self):
4951
{
5052
"Mkt-RF": [
5153
-3.36,
52-
3.40,
54+
3.4,
5355
6.31,
54-
2.00,
56+
2.0,
5557
-7.89,
5658
-5.57,
5759
6.93,
5860
-4.77,
5961
9.54,
6062
3.88,
61-
0.60,
63+
0.6,
6264
6.82,
6365
],
6466
"SMB": [
65-
0.40,
67+
0.4,
6668
1.19,
6769
1.48,
6870
4.87,
6971
0.09,
70-
-1.82,
71-
0.20,
72-
-3.00,
72+
-1.81,
73+
0.2,
74+
-3.0,
7375
3.96,
74-
1.14,
75-
3.77,
76+
1.13,
77+
3.76,
7678
0.73,
7779
],
7880
"HML": [
7981
0.43,
80-
3.23,
82+
3.22,
8183
2.21,
8284
2.89,
8385
-2.44,
84-
-4.70,
86+
-4.7,
8587
-0.31,
86-
-1.90,
88+
-1.9,
8789
-3.16,
88-
-2.43,
90+
-2.42,
8991
-0.96,
90-
3.70,
92+
3.69,
9193
],
9294
"RF": [
93-
0.00,
94-
0.00,
95+
0.0,
96+
0.0,
9597
0.01,
9698
0.01,
9799
0.01,
@@ -114,7 +116,7 @@ def test_f_f_research(self):
114116

115117
def test_me_breakpoints(self):
116118
results = web.DataReader(
117-
"ME_Breakpoints", "famafrench", start="2010-01-01", end="2010-12-01"
119+
"ME_Breakpoints", "famafrench", start="2010-01-01", end="2010-12-31"
118120
)
119121
assert isinstance(results, dict)
120122
assert len(results) == 2

0 commit comments

Comments
 (0)