Skip to content

Commit 8b03b58

Browse files
test: improve test coverage to 96%, remove unused FEATURES properties (#456)
* test: improve test coverage by 1%, remove unused FEATURES properties * add test session with no extras * 🦉 Updates from OwlBot See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * noextras take 2 * regex fix * 🦉 Updates from OwlBot See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * remove dead branches * no cover for impossible branches * move unit tests to correct directory Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
1 parent d120f8f commit 8b03b58

File tree

9 files changed

+92
-112
lines changed

9 files changed

+92
-112
lines changed

.coveragerc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ omit =
2222
google/cloud/__init__.py
2323

2424
[report]
25-
fail_under = 94
25+
fail_under = 96
2626
show_missing = True
2727
exclude_lines =
2828
# Re-enable the standard pragma

noxfile.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,11 @@ def default(session):
9595
constraints_path,
9696
)
9797

98-
session.install("-e", ".[tqdm]", "-c", constraints_path)
98+
if session.python == "3.9":
99+
extras = ""
100+
else:
101+
extras = "[tqdm]"
102+
session.install("-e", f".{extras}", "-c", constraints_path)
99103

100104
# Run py.test against the unit tests.
101105
session.run(
@@ -259,7 +263,7 @@ def cover(session):
259263
test runs (not system test runs), and then erases coverage data.
260264
"""
261265
session.install("coverage", "pytest-cov")
262-
session.run("coverage", "report", "--show-missing", "--fail-under=94")
266+
session.run("coverage", "report", "--show-missing", "--fail-under=96")
263267

264268
session.run("coverage", "erase")
265269

owlbot.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,17 @@
2929
# Add templated files
3030
# ----------------------------------------------------------------------------
3131

32+
extras_by_python = {
33+
# Use a middle version of Python to test when no extras are installed.
34+
"3.9": []
35+
}
3236
extras = ["tqdm"]
3337
templated_files = common.py_library(
3438
unit_test_python_versions=["3.7", "3.8", "3.9", "3.10"],
3539
system_test_python_versions=["3.7", "3.8", "3.9", "3.10"],
36-
cov_level=94,
40+
cov_level=96,
3741
unit_test_extras=extras,
42+
unit_test_extras_by_python=extras_by_python,
3843
system_test_extras=extras,
3944
intersphinx_dependencies={
4045
"pandas": "https://pandas.pydata.org/pandas-docs/stable/",
@@ -71,6 +76,11 @@
7176
["noxfile.py"], "--cov=google", "--cov=pandas_gbq",
7277
)
7378

79+
# Workaround for https://github.com/googleapis/synthtool/issues/1317
80+
s.replace(
81+
["noxfile.py"], r'extras = "\[\]"', 'extras = ""',
82+
)
83+
7484
s.replace(
7585
["noxfile.py"],
7686
r"@nox.session\(python=DEFAULT_PYTHON_VERSION\)\s+def cover\(session\):",

pandas_gbq/features.py

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,7 @@
55
"""Module for checking dependency versions and supported features."""
66

77
# https://github.com/googleapis/python-bigquery/blob/master/CHANGELOG.md
8-
BIGQUERY_MINIMUM_VERSION = "1.11.1"
9-
BIGQUERY_CLIENT_INFO_VERSION = "1.12.0"
10-
BIGQUERY_BQSTORAGE_VERSION = "1.24.0"
8+
BIGQUERY_MINIMUM_VERSION = "1.27.2"
119
BIGQUERY_ACCURATE_TIMESTAMP_VERSION = "2.6.0"
1210
BIGQUERY_FROM_DATAFRAME_CSV_VERSION = "2.6.0"
1311
BIGQUERY_SUPPORTS_BIGNUMERIC_VERSION = "2.10.0"
@@ -52,31 +50,13 @@ def bigquery_has_accurate_timestamp(self):
5250
min_version = pkg_resources.parse_version(BIGQUERY_ACCURATE_TIMESTAMP_VERSION)
5351
return self.bigquery_installed_version >= min_version
5452

55-
@property
56-
def bigquery_has_client_info(self):
57-
import pkg_resources
58-
59-
bigquery_client_info_version = pkg_resources.parse_version(
60-
BIGQUERY_CLIENT_INFO_VERSION
61-
)
62-
return self.bigquery_installed_version >= bigquery_client_info_version
63-
6453
@property
6554
def bigquery_has_bignumeric(self):
6655
import pkg_resources
6756

6857
min_version = pkg_resources.parse_version(BIGQUERY_SUPPORTS_BIGNUMERIC_VERSION)
6958
return self.bigquery_installed_version >= min_version
7059

71-
@property
72-
def bigquery_has_bqstorage(self):
73-
import pkg_resources
74-
75-
bigquery_bqstorage_version = pkg_resources.parse_version(
76-
BIGQUERY_BQSTORAGE_VERSION
77-
)
78-
return self.bigquery_installed_version >= bigquery_bqstorage_version
79-
8060
@property
8161
def bigquery_has_from_dataframe_with_csv(self):
8262
import pkg_resources

pandas_gbq/gbq.py

Lines changed: 19 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -18,21 +18,10 @@
1818
if typing.TYPE_CHECKING: # pragma: NO COVER
1919
import pandas
2020

21-
# Required dependencies, but treat as optional so that _test_google_api_imports
22-
# can provide a better error message.
23-
try:
24-
from google.api_core import exceptions as google_exceptions
25-
from google.cloud import bigquery
26-
except ImportError: # pragma: NO COVER
27-
bigquery = None
28-
google_exceptions = None
29-
3021
from pandas_gbq.exceptions import (
3122
AccessDenied,
3223
GenericGBQException,
33-
PerformanceWarning,
3424
)
35-
from pandas_gbq import features
3625
from pandas_gbq.features import FEATURES
3726
import pandas_gbq.schema
3827
import pandas_gbq.timestamp
@@ -48,32 +37,32 @@
4837
def _test_google_api_imports():
4938
try:
5039
import pkg_resources # noqa
51-
except ImportError as ex:
40+
except ImportError as ex: # pragma: NO COVER
5241
raise ImportError("pandas-gbq requires setuptools") from ex
5342

5443
try:
5544
import db_dtypes # noqa
56-
except ImportError as ex:
45+
except ImportError as ex: # pragma: NO COVER
5746
raise ImportError("pandas-gbq requires db-dtypes") from ex
5847

5948
try:
6049
import pydata_google_auth # noqa
61-
except ImportError as ex:
50+
except ImportError as ex: # pragma: NO COVER
6251
raise ImportError("pandas-gbq requires pydata-google-auth") from ex
6352

6453
try:
6554
from google_auth_oauthlib.flow import InstalledAppFlow # noqa
66-
except ImportError as ex:
55+
except ImportError as ex: # pragma: NO COVER
6756
raise ImportError("pandas-gbq requires google-auth-oauthlib") from ex
6857

6958
try:
7059
import google.auth # noqa
71-
except ImportError as ex:
60+
except ImportError as ex: # pragma: NO COVER
7261
raise ImportError("pandas-gbq requires google-auth") from ex
7362

7463
try:
7564
from google.cloud import bigquery # noqa
76-
except ImportError as ex:
65+
except ImportError as ex: # pragma: NO COVER
7766
raise ImportError("pandas-gbq requires google-cloud-bigquery") from ex
7867

7968

@@ -372,23 +361,17 @@ def sizeof_fmt(num, suffix="B"):
372361

373362
def get_client(self):
374363
import google.api_core.client_info
364+
from google.cloud import bigquery
375365
import pandas
376366

377367
client_info = google.api_core.client_info.ClientInfo(
378368
user_agent="pandas-{}".format(pandas.__version__)
379369
)
380-
381-
# In addition to new enough version of google-api-core, a new enough
382-
# version of google-cloud-bigquery is required to populate the
383-
# client_info.
384-
if FEATURES.bigquery_has_client_info:
385-
return bigquery.Client(
386-
project=self.project_id,
387-
credentials=self.credentials,
388-
client_info=client_info,
389-
)
390-
391-
return bigquery.Client(project=self.project_id, credentials=self.credentials)
370+
return bigquery.Client(
371+
project=self.project_id,
372+
credentials=self.credentials,
373+
client_info=client_info,
374+
)
392375

393376
@staticmethod
394377
def process_http_error(ex):
@@ -404,6 +387,8 @@ def download_table(
404387
progress_bar_type: Optional[str] = None,
405388
dtypes: Optional[Dict[str, Union[str, Any]]] = None,
406389
) -> "pandas.DataFrame":
390+
from google.cloud import bigquery
391+
407392
self._start_timer()
408393

409394
try:
@@ -424,6 +409,7 @@ def download_table(
424409
def run_query(self, query, max_results=None, progress_bar_type=None, **kwargs):
425410
from concurrent.futures import TimeoutError
426411
from google.auth.exceptions import RefreshError
412+
from google.cloud import bigquery
427413

428414
job_config = {
429415
"query": {
@@ -529,27 +515,11 @@ def _download_results(
529515
if user_dtypes is None:
530516
user_dtypes = {}
531517

532-
if self.use_bqstorage_api and not FEATURES.bigquery_has_bqstorage:
533-
warnings.warn(
534-
(
535-
"use_bqstorage_api was set, but have google-cloud-bigquery "
536-
"version {}. Requires google-cloud-bigquery version "
537-
"{} or later."
538-
).format(
539-
FEATURES.bigquery_installed_version,
540-
features.BIGQUERY_BQSTORAGE_VERSION,
541-
),
542-
PerformanceWarning,
543-
stacklevel=4,
544-
)
545-
546518
create_bqstorage_client = self.use_bqstorage_api
547519
if max_results is not None:
548520
create_bqstorage_client = False
549521

550522
to_dataframe_kwargs = {}
551-
if FEATURES.bigquery_has_bqstorage:
552-
to_dataframe_kwargs["create_bqstorage_client"] = create_bqstorage_client
553523
if FEATURES.bigquery_needs_date_as_object:
554524
to_dataframe_kwargs["date_as_object"] = True
555525

@@ -560,6 +530,7 @@ def _download_results(
560530
df = rows_iter.to_dataframe(
561531
dtypes=conversion_dtypes,
562532
progress_bar_type=progress_bar_type,
533+
create_bqstorage_client=create_bqstorage_client,
563534
**to_dataframe_kwargs,
564535
)
565536
except self.http_error as ex:
@@ -1051,6 +1022,9 @@ def to_gbq(
10511022

10521023
_test_google_api_imports()
10531024

1025+
from google.api_core import exceptions as google_exceptions
1026+
from google.cloud import bigquery
1027+
10541028
if verbose is not None and FEATURES.pandas_has_deprecated_verbose:
10551029
warnings.warn(
10561030
"verbose is deprecated and will be removed in "

tests/system/test_gbq.py

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -500,21 +500,6 @@ def test_timeout_configuration(self, project_id):
500500
configuration=config,
501501
)
502502

503-
def test_query_response_bytes(self):
504-
assert self.gbq_connector.sizeof_fmt(999) == "999.0 B"
505-
assert self.gbq_connector.sizeof_fmt(1024) == "1.0 KB"
506-
assert self.gbq_connector.sizeof_fmt(1099) == "1.1 KB"
507-
assert self.gbq_connector.sizeof_fmt(1044480) == "1020.0 KB"
508-
assert self.gbq_connector.sizeof_fmt(1048576) == "1.0 MB"
509-
assert self.gbq_connector.sizeof_fmt(1048576000) == "1000.0 MB"
510-
assert self.gbq_connector.sizeof_fmt(1073741824) == "1.0 GB"
511-
assert self.gbq_connector.sizeof_fmt(1.099512e12) == "1.0 TB"
512-
assert self.gbq_connector.sizeof_fmt(1.125900e15) == "1.0 PB"
513-
assert self.gbq_connector.sizeof_fmt(1.152922e18) == "1.0 EB"
514-
assert self.gbq_connector.sizeof_fmt(1.180592e21) == "1.0 ZB"
515-
assert self.gbq_connector.sizeof_fmt(1.208926e24) == "1.0 YB"
516-
assert self.gbq_connector.sizeof_fmt(1.208926e28) == "10000.0 YB"
517-
518503
def test_struct(self, project_id):
519504
query = """SELECT 1 int_field,
520505
STRUCT("a" as letter, 1 as num) struct_field"""

tests/unit/test_features.py

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ def fresh_bigquery_version(monkeypatch):
1616
@pytest.mark.parametrize(
1717
["bigquery_version", "expected"],
1818
[
19-
("1.11.1", False),
20-
("1.26.0", False),
19+
("1.27.2", False),
20+
("1.99.100", False),
2121
("2.5.4", False),
2222
("2.6.0", True),
2323
("2.6.1", True),
@@ -34,8 +34,8 @@ def test_bigquery_has_accurate_timestamp(monkeypatch, bigquery_version, expected
3434
@pytest.mark.parametrize(
3535
["bigquery_version", "expected"],
3636
[
37-
("1.11.1", False),
38-
("1.26.0", False),
37+
("1.27.2", False),
38+
("1.99.100", False),
3939
("2.9.999", False),
4040
("2.10.0", True),
4141
("2.12.0", True),
@@ -52,8 +52,8 @@ def test_bigquery_has_bignumeric(monkeypatch, bigquery_version, expected):
5252
@pytest.mark.parametrize(
5353
["bigquery_version", "expected"],
5454
[
55-
("1.11.1", False),
56-
("1.26.0", False),
55+
("1.27.2", False),
56+
("1.99.100", False),
5757
("2.5.4", False),
5858
("2.6.0", True),
5959
("2.6.1", True),
@@ -69,7 +69,13 @@ def test_bigquery_has_from_dataframe_with_csv(monkeypatch, bigquery_version, exp
6969

7070
@pytest.mark.parametrize(
7171
["bigquery_version", "expected"],
72-
[("1.26.0", True), ("2.12.0", True), ("3.0.0", False), ("3.1.0", False)],
72+
[
73+
("1.27.2", True),
74+
("1.99.100", True),
75+
("2.12.0", True),
76+
("3.0.0", False),
77+
("3.1.0", False),
78+
],
7379
)
7480
def test_bigquery_needs_date_as_object(monkeypatch, bigquery_version, expected):
7581
import google.cloud.bigquery

tests/unit/test_gbq.py

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -109,25 +109,8 @@ def test__is_query(query_or_table, expected):
109109
assert result == expected
110110

111111

112-
def test_GbqConnector_get_client_w_old_bq(monkeypatch, mock_bigquery_client):
113-
gbq._test_google_api_imports()
114-
connector = _make_connector()
115-
monkeypatch.setattr(
116-
type(FEATURES),
117-
"bigquery_has_client_info",
118-
mock.PropertyMock(return_value=False),
119-
)
120-
121-
connector.get_client()
122-
123-
# No client_info argument.
124-
mock_bigquery_client.assert_called_with(credentials=mock.ANY, project=mock.ANY)
125-
126-
127112
def test_GbqConnector_get_client_w_new_bq(mock_bigquery_client):
128113
gbq._test_google_api_imports()
129-
if not FEATURES.bigquery_has_client_info:
130-
pytest.skip("google-cloud-bigquery missing client_info feature")
131114
pytest.importorskip("google.api_core.client_info")
132115

133116
connector = _make_connector()
@@ -606,9 +589,6 @@ def test_read_gbq_passes_dtypes(mock_bigquery_client, mock_service_account_crede
606589
def test_read_gbq_use_bqstorage_api(
607590
mock_bigquery_client, mock_service_account_credentials
608591
):
609-
if not FEATURES.bigquery_has_bqstorage: # pragma: NO COVER
610-
pytest.skip("requires BigQuery Storage API")
611-
612592
mock_service_account_credentials.project_id = "service_account_project_id"
613593
df = gbq.read_gbq(
614594
"SELECT 1 AS int_col",
@@ -716,3 +696,25 @@ def test_read_gbq_with_list_rows_error_translates_exception(
716696
"my-project.my_dataset.read_gbq_table",
717697
credentials=mock_service_account_credentials,
718698
)
699+
700+
701+
@pytest.mark.parametrize(
702+
["size_in_bytes", "formatted_text"],
703+
[
704+
(999, "999.0 B"),
705+
(1024, "1.0 KB"),
706+
(1099, "1.1 KB"),
707+
(1044480, "1020.0 KB"),
708+
(1048576, "1.0 MB"),
709+
(1048576000, "1000.0 MB"),
710+
(1073741824, "1.0 GB"),
711+
(1.099512e12, "1.0 TB"),
712+
(1.125900e15, "1.0 PB"),
713+
(1.152922e18, "1.0 EB"),
714+
(1.180592e21, "1.0 ZB"),
715+
(1.208926e24, "1.0 YB"),
716+
(1.208926e28, "10000.0 YB"),
717+
],
718+
)
719+
def test_query_response_bytes(size_in_bytes, formatted_text):
720+
assert gbq.GbqConnector.sizeof_fmt(size_in_bytes) == formatted_text

0 commit comments

Comments
 (0)