diff --git a/docs/user-guide.rst b/docs/user-guide.rst index e3c8e5ac..34aeeb5d 100644 --- a/docs/user-guide.rst +++ b/docs/user-guide.rst @@ -248,6 +248,23 @@ Alternatively, use ``gspread.utils.to_records()`` for more control over headers: # {'fruit': 'banana', 'alternate name': 'yellow stick', 'tastiness': 'quite'} +Getting All Values From Every Worksheet at Once +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Using ``get_all_worksheet_values()`` (fetches every worksheet in a single API call): + +.. code:: python + + all_values = spreadsheet.get_all_worksheet_values() + # {'Sheet1': [['a', 'b'], ['c', 'd']], 'Sheet 2': [['1', '2']]} + +Optionally, pass ``skip_worksheet_titles`` to leave out worksheets you don't need: + +.. code:: python + + all_values = spreadsheet.get_all_worksheet_values(skip_worksheet_titles=["Sheet1"]) + + Loading Worksheet Data into a DataFrame ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/gspread/spreadsheet.py b/gspread/spreadsheet.py index ec77121a..8d7fd9f5 100644 --- a/gspread/spreadsheet.py +++ b/gspread/spreadsheet.py @@ -15,7 +15,7 @@ from .exceptions import WorksheetNotFound from .http_client import HTTPClient, ParamsType from .urls import DRIVE_FILES_API_V3_URL, SPREADSHEET_DRIVE_URL -from .utils import ExportFormat, finditem +from .utils import ExportFormat, absolute_range_name, extract_title_from_range, finditem from .worksheet import Worksheet @@ -284,6 +284,36 @@ def get_worksheet_by_id(self, id: Union[str, int]) -> Worksheet: except (StopIteration, KeyError): raise WorksheetNotFound("id {} not found".format(worksheet_id_int)) + def get_all_worksheet_values( + self, + skip_worksheet_titles: Optional[List[str]] = None, + params: Optional[ParamsType] = None, + ) -> Dict: + """Returns all values from all worksheets in a single API call. + + :param list skip_worksheet_titles: (optional) A list of worksheet titles to skip. + :returns: A dict mapping worksheet title to its values. + :rtype: dict + """ + if skip_worksheet_titles is None: + skip_worksheet_titles = [] + + ranges = [ + absolute_range_name(ws.title) + for ws in self.worksheets() + if ws.title not in skip_worksheet_titles + ] + + if not ranges: + return {} + + response = self.values_batch_get(ranges, params=params) + + return { + extract_title_from_range(vr["range"]): vr.get("values", []) + for vr in response.get("valueRanges", []) + } + def worksheets(self, exclude_hidden: bool = False) -> List[Worksheet]: """Returns a list of all :class:`worksheets ` in a spreadsheet. diff --git a/gspread/utils.py b/gspread/utils.py index 6daac861..1256196b 100644 --- a/gspread/utils.py +++ b/gspread/utils.py @@ -42,6 +42,7 @@ CELL_ADDR_RE = re.compile(r"([A-Za-z]+)([1-9]\d*)") A1_ADDR_ROW_COL_RE = re.compile(r"([A-Za-z]+)?([1-9]\d*)?$") A1_ADDR_FULL_RE = re.compile(r"[A-Za-z]+\d+:[A-Za-z]+\d+") # e.g. A1:B2 not A1:B +SHEET_TITLE_RE = re.compile(r"'((?:[^']|'')+)'!|([^!]+)!") URL_KEY_V1_RE = re.compile(r"key=([^&#]+)") URL_KEY_V2_RE = re.compile(r"/spreadsheets/d/([a-zA-Z0-9-_]+)") @@ -365,7 +366,7 @@ def rowcol_to_a1(row: int, col: int) -> str: column_label = "" while div: - (div, mod) = divmod(div, 26) + div, mod = divmod(div, 26) if mod == 0: mod = 26 div -= 1 @@ -561,7 +562,7 @@ def column_letter_to_index(column: str) -> int: gspread.exceptions.InvalidInputValue: invalid value: !@#$%^&, must be a column letter """ try: - (_, index) = _a1_to_rowcol_unbounded(column) + _, index = _a1_to_rowcol_unbounded(column) except IncorrectCellLabel: # make it coherent and raise the same exception in case of any error # from user input value @@ -622,6 +623,22 @@ def extract_id_from_url(url: str) -> str: raise NoValidUrlKeyFound +def extract_title_from_range(range_string: str) -> str: + """Extracts the worksheet title from a range string. + + :param str range_string: A range string e.g. ``'Sheet Name'!A1:Z100`` or ``Sheet1!A1:Z100`` + :returns: The worksheet title. + :rtype: str + + :raises: + :class:`~gspread.exceptions.InvalidInputValue`: if the title cannot be extracted. + """ + match = SHEET_TITLE_RE.match(range_string) + if match: + return (match.group(1) or match.group(2)).replace("''", "'") + raise InvalidInputValue + + def wid_to_gid(wid: str) -> str: """Calculate gid of a worksheet from its wid.""" widval = wid[1:] if len(wid) > 3 else wid diff --git a/tests/cassettes/SpreadsheetTest.test_get_all_worksheet_values.json b/tests/cassettes/SpreadsheetTest.test_get_all_worksheet_values.json new file mode 100644 index 00000000..bc72ece3 --- /dev/null +++ b/tests/cassettes/SpreadsheetTest.test_get_all_worksheet_values.json @@ -0,0 +1,807 @@ +{ + "version": 1, + "interactions": [ + { + "request": { + "method": "POST", + "uri": "https://www.googleapis.com/drive/v3/files?supportsAllDrives=True", + "body": "{\"name\": \"Test SpreadsheetTest test_get_all_worksheet_values\", \"mimeType\": \"application/vnd.google-apps.spreadsheet\"}", + "headers": { + "User-Agent": [ + "python-requests/2.34.2" + ], + "Accept-Encoding": [ + "gzip, deflate" + ], + "Accept": [ + "*/*" + ], + "Connection": [ + "keep-alive" + ], + "Content-Length": [ + "117" + ], + "Content-Type": [ + "application/json" + ], + "authorization": [ + "" + ] + } + }, + "response": { + "status": { + "code": 200, + "message": "OK" + }, + "headers": { + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "X-XSS-Protection": [ + "0" + ], + "Vary": [ + "Origin, X-Origin" + ], + "Date": [ + "Mon, 15 Jun 2026 13:57:17 GMT" + ], + "Transfer-Encoding": [ + "chunked" + ], + "Alt-Svc": [ + "h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000" + ], + "Pragma": [ + "no-cache" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "Server": [ + "ESF" + ], + "content-length": [ + "204" + ] + }, + "body": { + "string": "{\n \"kind\": \"drive#file\",\n \"id\": \"1JDlVaNWQ08apISMjEBgEFJnqzKfRLhqHNbcAxoUfpsE\",\n \"name\": \"Test SpreadsheetTest test_get_all_worksheet_values\",\n \"mimeType\": \"application/vnd.google-apps.spreadsheet\"\n}\n" + } + } + }, + { + "request": { + "method": "GET", + "uri": "https://sheets.googleapis.com/v4/spreadsheets/1JDlVaNWQ08apISMjEBgEFJnqzKfRLhqHNbcAxoUfpsE?includeGridData=false", + "body": null, + "headers": { + "User-Agent": [ + "python-requests/2.34.2" + ], + "Accept-Encoding": [ + "gzip, deflate" + ], + "Accept": [ + "*/*" + ], + "Connection": [ + "keep-alive" + ], + "authorization": [ + "" + ] + } + }, + "response": { + "status": { + "code": 200, + "message": "OK" + }, + "headers": { + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "X-XSS-Protection": [ + "0" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "Date": [ + "Mon, 15 Jun 2026 13:57:18 GMT" + ], + "Transfer-Encoding": [ + "chunked" + ], + "Alt-Svc": [ + "h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000" + ], + "x-l2-request-path": [ + "l2-managed-6" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "Server": [ + "ESF" + ], + "content-length": [ + "3348" + ] + }, + "body": { + "string": "{\n \"spreadsheetId\": \"1JDlVaNWQ08apISMjEBgEFJnqzKfRLhqHNbcAxoUfpsE\",\n \"properties\": {\n \"title\": \"Test SpreadsheetTest test_get_all_worksheet_values\",\n \"locale\": \"en_US\",\n \"autoRecalc\": \"ON_CHANGE\",\n \"timeZone\": \"Etc/GMT\",\n \"defaultFormat\": {\n \"backgroundColor\": {\n \"red\": 1,\n \"green\": 1,\n \"blue\": 1\n },\n \"padding\": {\n \"top\": 2,\n \"right\": 3,\n \"bottom\": 2,\n \"left\": 3\n },\n \"verticalAlignment\": \"BOTTOM\",\n \"wrapStrategy\": \"OVERFLOW_CELL\",\n \"textFormat\": {\n \"foregroundColor\": {},\n \"fontFamily\": \"arial,sans,sans-serif\",\n \"fontSize\": 10,\n \"bold\": false,\n \"italic\": false,\n \"strikethrough\": false,\n \"underline\": false,\n \"foregroundColorStyle\": {\n \"rgbColor\": {}\n }\n },\n \"backgroundColorStyle\": {\n \"rgbColor\": {\n \"red\": 1,\n \"green\": 1,\n \"blue\": 1\n }\n }\n },\n \"spreadsheetTheme\": {\n \"primaryFontFamily\": \"Arial\",\n \"themeColors\": [\n {\n \"colorType\": \"TEXT\",\n \"color\": {\n \"rgbColor\": {}\n }\n },\n {\n \"colorType\": \"BACKGROUND\",\n \"color\": {\n \"rgbColor\": {\n \"red\": 1,\n \"green\": 1,\n \"blue\": 1\n }\n }\n },\n {\n \"colorType\": \"ACCENT1\",\n \"color\": {\n \"rgbColor\": {\n \"red\": 0.25882354,\n \"green\": 0.52156866,\n \"blue\": 0.95686275\n }\n }\n },\n {\n \"colorType\": \"ACCENT2\",\n \"color\": {\n \"rgbColor\": {\n \"red\": 0.91764706,\n \"green\": 0.2627451,\n \"blue\": 0.20784314\n }\n }\n },\n {\n \"colorType\": \"ACCENT3\",\n \"color\": {\n \"rgbColor\": {\n \"red\": 0.9843137,\n \"green\": 0.7372549,\n \"blue\": 0.015686275\n }\n }\n },\n {\n \"colorType\": \"ACCENT4\",\n \"color\": {\n \"rgbColor\": {\n \"red\": 0.20392157,\n \"green\": 0.65882355,\n \"blue\": 0.3254902\n }\n }\n },\n {\n \"colorType\": \"ACCENT5\",\n \"color\": {\n \"rgbColor\": {\n \"red\": 1,\n \"green\": 0.42745098,\n \"blue\": 0.003921569\n }\n }\n },\n {\n \"colorType\": \"ACCENT6\",\n \"color\": {\n \"rgbColor\": {\n \"red\": 0.27450982,\n \"green\": 0.7411765,\n \"blue\": 0.7764706\n }\n }\n },\n {\n \"colorType\": \"LINK\",\n \"color\": {\n \"rgbColor\": {\n \"red\": 0.06666667,\n \"green\": 0.33333334,\n \"blue\": 0.8\n }\n }\n }\n ]\n }\n },\n \"sheets\": [\n {\n \"properties\": {\n \"sheetId\": 0,\n \"title\": \"Sheet1\",\n \"index\": 0,\n \"sheetType\": \"GRID\",\n \"gridProperties\": {\n \"rowCount\": 1000,\n \"columnCount\": 26\n }\n }\n }\n ],\n \"spreadsheetUrl\": \"https://docs.google.com/spreadsheets/d/1JDlVaNWQ08apISMjEBgEFJnqzKfRLhqHNbcAxoUfpsE/edit\"\n}\n" + } + } + }, + { + "request": { + "method": "GET", + "uri": "https://sheets.googleapis.com/v4/spreadsheets/1JDlVaNWQ08apISMjEBgEFJnqzKfRLhqHNbcAxoUfpsE?includeGridData=false", + "body": null, + "headers": { + "User-Agent": [ + "python-requests/2.34.2" + ], + "Accept-Encoding": [ + "gzip, deflate" + ], + "Accept": [ + "*/*" + ], + "Connection": [ + "keep-alive" + ], + "authorization": [ + "" + ] + } + }, + "response": { + "status": { + "code": 200, + "message": "OK" + }, + "headers": { + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "X-XSS-Protection": [ + "0" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "Date": [ + "Mon, 15 Jun 2026 13:57:18 GMT" + ], + "Transfer-Encoding": [ + "chunked" + ], + "Alt-Svc": [ + "h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000" + ], + "x-l2-request-path": [ + "l2-managed-6" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "Server": [ + "ESF" + ], + "content-length": [ + "3348" + ] + }, + "body": { + "string": "{\n \"spreadsheetId\": \"1JDlVaNWQ08apISMjEBgEFJnqzKfRLhqHNbcAxoUfpsE\",\n \"properties\": {\n \"title\": \"Test SpreadsheetTest test_get_all_worksheet_values\",\n \"locale\": \"en_US\",\n \"autoRecalc\": \"ON_CHANGE\",\n \"timeZone\": \"Etc/GMT\",\n \"defaultFormat\": {\n \"backgroundColor\": {\n \"red\": 1,\n \"green\": 1,\n \"blue\": 1\n },\n \"padding\": {\n \"top\": 2,\n \"right\": 3,\n \"bottom\": 2,\n \"left\": 3\n },\n \"verticalAlignment\": \"BOTTOM\",\n \"wrapStrategy\": \"OVERFLOW_CELL\",\n \"textFormat\": {\n \"foregroundColor\": {},\n \"fontFamily\": \"arial,sans,sans-serif\",\n \"fontSize\": 10,\n \"bold\": false,\n \"italic\": false,\n \"strikethrough\": false,\n \"underline\": false,\n \"foregroundColorStyle\": {\n \"rgbColor\": {}\n }\n },\n \"backgroundColorStyle\": {\n \"rgbColor\": {\n \"red\": 1,\n \"green\": 1,\n \"blue\": 1\n }\n }\n },\n \"spreadsheetTheme\": {\n \"primaryFontFamily\": \"Arial\",\n \"themeColors\": [\n {\n \"colorType\": \"TEXT\",\n \"color\": {\n \"rgbColor\": {}\n }\n },\n {\n \"colorType\": \"BACKGROUND\",\n \"color\": {\n \"rgbColor\": {\n \"red\": 1,\n \"green\": 1,\n \"blue\": 1\n }\n }\n },\n {\n \"colorType\": \"ACCENT1\",\n \"color\": {\n \"rgbColor\": {\n \"red\": 0.25882354,\n \"green\": 0.52156866,\n \"blue\": 0.95686275\n }\n }\n },\n {\n \"colorType\": \"ACCENT2\",\n \"color\": {\n \"rgbColor\": {\n \"red\": 0.91764706,\n \"green\": 0.2627451,\n \"blue\": 0.20784314\n }\n }\n },\n {\n \"colorType\": \"ACCENT3\",\n \"color\": {\n \"rgbColor\": {\n \"red\": 0.9843137,\n \"green\": 0.7372549,\n \"blue\": 0.015686275\n }\n }\n },\n {\n \"colorType\": \"ACCENT4\",\n \"color\": {\n \"rgbColor\": {\n \"red\": 0.20392157,\n \"green\": 0.65882355,\n \"blue\": 0.3254902\n }\n }\n },\n {\n \"colorType\": \"ACCENT5\",\n \"color\": {\n \"rgbColor\": {\n \"red\": 1,\n \"green\": 0.42745098,\n \"blue\": 0.003921569\n }\n }\n },\n {\n \"colorType\": \"ACCENT6\",\n \"color\": {\n \"rgbColor\": {\n \"red\": 0.27450982,\n \"green\": 0.7411765,\n \"blue\": 0.7764706\n }\n }\n },\n {\n \"colorType\": \"LINK\",\n \"color\": {\n \"rgbColor\": {\n \"red\": 0.06666667,\n \"green\": 0.33333334,\n \"blue\": 0.8\n }\n }\n }\n ]\n }\n },\n \"sheets\": [\n {\n \"properties\": {\n \"sheetId\": 0,\n \"title\": \"Sheet1\",\n \"index\": 0,\n \"sheetType\": \"GRID\",\n \"gridProperties\": {\n \"rowCount\": 1000,\n \"columnCount\": 26\n }\n }\n }\n ],\n \"spreadsheetUrl\": \"https://docs.google.com/spreadsheets/d/1JDlVaNWQ08apISMjEBgEFJnqzKfRLhqHNbcAxoUfpsE/edit\"\n}\n" + } + } + }, + { + "request": { + "method": "PUT", + "uri": "https://sheets.googleapis.com/v4/spreadsheets/1JDlVaNWQ08apISMjEBgEFJnqzKfRLhqHNbcAxoUfpsE/values/%27Sheet1%27%21A1%3AB2?valueInputOption=RAW", + "body": "{\"values\": [[\"a\", \"b\"], [\"c\", \"d\"]], \"majorDimension\": null}", + "headers": { + "User-Agent": [ + "python-requests/2.34.2" + ], + "Accept-Encoding": [ + "gzip, deflate" + ], + "Accept": [ + "*/*" + ], + "Connection": [ + "keep-alive" + ], + "Content-Length": [ + "60" + ], + "Content-Type": [ + "application/json" + ], + "authorization": [ + "" + ] + } + }, + "response": { + "status": { + "code": 200, + "message": "OK" + }, + "headers": { + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "X-XSS-Protection": [ + "0" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "Date": [ + "Mon, 15 Jun 2026 13:57:18 GMT" + ], + "Transfer-Encoding": [ + "chunked" + ], + "Alt-Svc": [ + "h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000" + ], + "x-l2-request-path": [ + "l2-managed-6" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "Server": [ + "ESF" + ], + "content-length": [ + "168" + ] + }, + "body": { + "string": "{\n \"spreadsheetId\": \"1JDlVaNWQ08apISMjEBgEFJnqzKfRLhqHNbcAxoUfpsE\",\n \"updatedRange\": \"Sheet1!A1:B2\",\n \"updatedRows\": 2,\n \"updatedColumns\": 2,\n \"updatedCells\": 4\n}\n" + } + } + }, + { + "request": { + "method": "POST", + "uri": "https://sheets.googleapis.com/v4/spreadsheets/1JDlVaNWQ08apISMjEBgEFJnqzKfRLhqHNbcAxoUfpsE:batchUpdate", + "body": "{\"requests\": [{\"addSheet\": {\"properties\": {\"title\": \"Sheet 2\", \"sheetType\": \"GRID\", \"gridProperties\": {\"rowCount\": 10, \"columnCount\": 10}}}}]}", + "headers": { + "User-Agent": [ + "python-requests/2.34.2" + ], + "Accept-Encoding": [ + "gzip, deflate" + ], + "Accept": [ + "*/*" + ], + "Connection": [ + "keep-alive" + ], + "Content-Length": [ + "142" + ], + "Content-Type": [ + "application/json" + ], + "authorization": [ + "" + ] + } + }, + "response": { + "status": { + "code": 200, + "message": "OK" + }, + "headers": { + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "X-XSS-Protection": [ + "0" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "Date": [ + "Mon, 15 Jun 2026 13:57:18 GMT" + ], + "Transfer-Encoding": [ + "chunked" + ], + "Alt-Svc": [ + "h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000" + ], + "x-l2-request-path": [ + "l2-managed-6" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "Server": [ + "ESF" + ], + "content-length": [ + "380" + ] + }, + "body": { + "string": "{\n \"spreadsheetId\": \"1JDlVaNWQ08apISMjEBgEFJnqzKfRLhqHNbcAxoUfpsE\",\n \"replies\": [\n {\n \"addSheet\": {\n \"properties\": {\n \"sheetId\": 1865970947,\n \"title\": \"Sheet 2\",\n \"index\": 1,\n \"sheetType\": \"GRID\",\n \"gridProperties\": {\n \"rowCount\": 10,\n \"columnCount\": 10\n }\n }\n }\n }\n ]\n}\n" + } + } + }, + { + "request": { + "method": "PUT", + "uri": "https://sheets.googleapis.com/v4/spreadsheets/1JDlVaNWQ08apISMjEBgEFJnqzKfRLhqHNbcAxoUfpsE/values/%27Sheet%202%27%21A1%3AB1?valueInputOption=RAW", + "body": "{\"values\": [[\"1\", \"2\"]], \"majorDimension\": null}", + "headers": { + "User-Agent": [ + "python-requests/2.34.2" + ], + "Accept-Encoding": [ + "gzip, deflate" + ], + "Accept": [ + "*/*" + ], + "Connection": [ + "keep-alive" + ], + "Content-Length": [ + "48" + ], + "Content-Type": [ + "application/json" + ], + "authorization": [ + "" + ] + } + }, + "response": { + "status": { + "code": 200, + "message": "OK" + }, + "headers": { + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "X-XSS-Protection": [ + "0" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "Date": [ + "Mon, 15 Jun 2026 13:57:18 GMT" + ], + "Transfer-Encoding": [ + "chunked" + ], + "Alt-Svc": [ + "h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000" + ], + "x-l2-request-path": [ + "l2-managed-6" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "Server": [ + "ESF" + ], + "content-length": [ + "171" + ] + }, + "body": { + "string": "{\n \"spreadsheetId\": \"1JDlVaNWQ08apISMjEBgEFJnqzKfRLhqHNbcAxoUfpsE\",\n \"updatedRange\": \"'Sheet 2'!A1:B1\",\n \"updatedRows\": 1,\n \"updatedColumns\": 2,\n \"updatedCells\": 2\n}\n" + } + } + }, + { + "request": { + "method": "GET", + "uri": "https://sheets.googleapis.com/v4/spreadsheets/1JDlVaNWQ08apISMjEBgEFJnqzKfRLhqHNbcAxoUfpsE?includeGridData=false", + "body": null, + "headers": { + "User-Agent": [ + "python-requests/2.34.2" + ], + "Accept-Encoding": [ + "gzip, deflate" + ], + "Accept": [ + "*/*" + ], + "Connection": [ + "keep-alive" + ], + "authorization": [ + "" + ] + } + }, + "response": { + "status": { + "code": 200, + "message": "OK" + }, + "headers": { + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "X-XSS-Protection": [ + "0" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "Date": [ + "Mon, 15 Jun 2026 13:57:18 GMT" + ], + "Transfer-Encoding": [ + "chunked" + ], + "Alt-Svc": [ + "h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000" + ], + "x-l2-request-path": [ + "l2-managed-6" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "Server": [ + "ESF" + ], + "content-length": [ + "3591" + ] + }, + "body": { + "string": "{\n \"spreadsheetId\": \"1JDlVaNWQ08apISMjEBgEFJnqzKfRLhqHNbcAxoUfpsE\",\n \"properties\": {\n \"title\": \"Test SpreadsheetTest test_get_all_worksheet_values\",\n \"locale\": \"en_US\",\n \"autoRecalc\": \"ON_CHANGE\",\n \"timeZone\": \"Etc/GMT\",\n \"defaultFormat\": {\n \"backgroundColor\": {\n \"red\": 1,\n \"green\": 1,\n \"blue\": 1\n },\n \"padding\": {\n \"top\": 2,\n \"right\": 3,\n \"bottom\": 2,\n \"left\": 3\n },\n \"verticalAlignment\": \"BOTTOM\",\n \"wrapStrategy\": \"OVERFLOW_CELL\",\n \"textFormat\": {\n \"foregroundColor\": {},\n \"fontFamily\": \"arial,sans,sans-serif\",\n \"fontSize\": 10,\n \"bold\": false,\n \"italic\": false,\n \"strikethrough\": false,\n \"underline\": false,\n \"foregroundColorStyle\": {\n \"rgbColor\": {}\n }\n },\n \"backgroundColorStyle\": {\n \"rgbColor\": {\n \"red\": 1,\n \"green\": 1,\n \"blue\": 1\n }\n }\n },\n \"spreadsheetTheme\": {\n \"primaryFontFamily\": \"Arial\",\n \"themeColors\": [\n {\n \"colorType\": \"TEXT\",\n \"color\": {\n \"rgbColor\": {}\n }\n },\n {\n \"colorType\": \"BACKGROUND\",\n \"color\": {\n \"rgbColor\": {\n \"red\": 1,\n \"green\": 1,\n \"blue\": 1\n }\n }\n },\n {\n \"colorType\": \"ACCENT1\",\n \"color\": {\n \"rgbColor\": {\n \"red\": 0.25882354,\n \"green\": 0.52156866,\n \"blue\": 0.95686275\n }\n }\n },\n {\n \"colorType\": \"ACCENT2\",\n \"color\": {\n \"rgbColor\": {\n \"red\": 0.91764706,\n \"green\": 0.2627451,\n \"blue\": 0.20784314\n }\n }\n },\n {\n \"colorType\": \"ACCENT3\",\n \"color\": {\n \"rgbColor\": {\n \"red\": 0.9843137,\n \"green\": 0.7372549,\n \"blue\": 0.015686275\n }\n }\n },\n {\n \"colorType\": \"ACCENT4\",\n \"color\": {\n \"rgbColor\": {\n \"red\": 0.20392157,\n \"green\": 0.65882355,\n \"blue\": 0.3254902\n }\n }\n },\n {\n \"colorType\": \"ACCENT5\",\n \"color\": {\n \"rgbColor\": {\n \"red\": 1,\n \"green\": 0.42745098,\n \"blue\": 0.003921569\n }\n }\n },\n {\n \"colorType\": \"ACCENT6\",\n \"color\": {\n \"rgbColor\": {\n \"red\": 0.27450982,\n \"green\": 0.7411765,\n \"blue\": 0.7764706\n }\n }\n },\n {\n \"colorType\": \"LINK\",\n \"color\": {\n \"rgbColor\": {\n \"red\": 0.06666667,\n \"green\": 0.33333334,\n \"blue\": 0.8\n }\n }\n }\n ]\n }\n },\n \"sheets\": [\n {\n \"properties\": {\n \"sheetId\": 0,\n \"title\": \"Sheet1\",\n \"index\": 0,\n \"sheetType\": \"GRID\",\n \"gridProperties\": {\n \"rowCount\": 1000,\n \"columnCount\": 26\n }\n }\n },\n {\n \"properties\": {\n \"sheetId\": 1865970947,\n \"title\": \"Sheet 2\",\n \"index\": 1,\n \"sheetType\": \"GRID\",\n \"gridProperties\": {\n \"rowCount\": 10,\n \"columnCount\": 10\n }\n }\n }\n ],\n \"spreadsheetUrl\": \"https://docs.google.com/spreadsheets/d/1JDlVaNWQ08apISMjEBgEFJnqzKfRLhqHNbcAxoUfpsE/edit\"\n}\n" + } + } + }, + { + "request": { + "method": "GET", + "uri": "https://sheets.googleapis.com/v4/spreadsheets/1JDlVaNWQ08apISMjEBgEFJnqzKfRLhqHNbcAxoUfpsE/values:batchGet?ranges=%27Sheet1%27&ranges=%27Sheet+2%27", + "body": null, + "headers": { + "User-Agent": [ + "python-requests/2.34.2" + ], + "Accept-Encoding": [ + "gzip, deflate" + ], + "Accept": [ + "*/*" + ], + "Connection": [ + "keep-alive" + ], + "authorization": [ + "" + ] + } + }, + "response": { + "status": { + "code": 200, + "message": "OK" + }, + "headers": { + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "X-XSS-Protection": [ + "0" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "Date": [ + "Mon, 15 Jun 2026 13:57:19 GMT" + ], + "Transfer-Encoding": [ + "chunked" + ], + "Alt-Svc": [ + "h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000" + ], + "x-l2-request-path": [ + "l2-managed-6" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "Server": [ + "ESF" + ], + "content-length": [ + "452" + ] + }, + "body": { + "string": "{\n \"spreadsheetId\": \"1JDlVaNWQ08apISMjEBgEFJnqzKfRLhqHNbcAxoUfpsE\",\n \"valueRanges\": [\n {\n \"range\": \"Sheet1!A1:Z1000\",\n \"majorDimension\": \"ROWS\",\n \"values\": [\n [\n \"a\",\n \"b\"\n ],\n [\n \"c\",\n \"d\"\n ]\n ]\n },\n {\n \"range\": \"'Sheet 2'!A1:J10\",\n \"majorDimension\": \"ROWS\",\n \"values\": [\n [\n \"1\",\n \"2\"\n ]\n ]\n }\n ]\n}\n" + } + } + }, + { + "request": { + "method": "GET", + "uri": "https://sheets.googleapis.com/v4/spreadsheets/1JDlVaNWQ08apISMjEBgEFJnqzKfRLhqHNbcAxoUfpsE?includeGridData=false", + "body": null, + "headers": { + "User-Agent": [ + "python-requests/2.34.2" + ], + "Accept-Encoding": [ + "gzip, deflate" + ], + "Accept": [ + "*/*" + ], + "Connection": [ + "keep-alive" + ], + "authorization": [ + "" + ] + } + }, + "response": { + "status": { + "code": 200, + "message": "OK" + }, + "headers": { + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "X-XSS-Protection": [ + "0" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "Date": [ + "Mon, 15 Jun 2026 13:57:19 GMT" + ], + "Transfer-Encoding": [ + "chunked" + ], + "Alt-Svc": [ + "h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000" + ], + "x-l2-request-path": [ + "l2-managed-6" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "Server": [ + "ESF" + ], + "content-length": [ + "3591" + ] + }, + "body": { + "string": "{\n \"spreadsheetId\": \"1JDlVaNWQ08apISMjEBgEFJnqzKfRLhqHNbcAxoUfpsE\",\n \"properties\": {\n \"title\": \"Test SpreadsheetTest test_get_all_worksheet_values\",\n \"locale\": \"en_US\",\n \"autoRecalc\": \"ON_CHANGE\",\n \"timeZone\": \"Etc/GMT\",\n \"defaultFormat\": {\n \"backgroundColor\": {\n \"red\": 1,\n \"green\": 1,\n \"blue\": 1\n },\n \"padding\": {\n \"top\": 2,\n \"right\": 3,\n \"bottom\": 2,\n \"left\": 3\n },\n \"verticalAlignment\": \"BOTTOM\",\n \"wrapStrategy\": \"OVERFLOW_CELL\",\n \"textFormat\": {\n \"foregroundColor\": {},\n \"fontFamily\": \"arial,sans,sans-serif\",\n \"fontSize\": 10,\n \"bold\": false,\n \"italic\": false,\n \"strikethrough\": false,\n \"underline\": false,\n \"foregroundColorStyle\": {\n \"rgbColor\": {}\n }\n },\n \"backgroundColorStyle\": {\n \"rgbColor\": {\n \"red\": 1,\n \"green\": 1,\n \"blue\": 1\n }\n }\n },\n \"spreadsheetTheme\": {\n \"primaryFontFamily\": \"Arial\",\n \"themeColors\": [\n {\n \"colorType\": \"TEXT\",\n \"color\": {\n \"rgbColor\": {}\n }\n },\n {\n \"colorType\": \"BACKGROUND\",\n \"color\": {\n \"rgbColor\": {\n \"red\": 1,\n \"green\": 1,\n \"blue\": 1\n }\n }\n },\n {\n \"colorType\": \"ACCENT1\",\n \"color\": {\n \"rgbColor\": {\n \"red\": 0.25882354,\n \"green\": 0.52156866,\n \"blue\": 0.95686275\n }\n }\n },\n {\n \"colorType\": \"ACCENT2\",\n \"color\": {\n \"rgbColor\": {\n \"red\": 0.91764706,\n \"green\": 0.2627451,\n \"blue\": 0.20784314\n }\n }\n },\n {\n \"colorType\": \"ACCENT3\",\n \"color\": {\n \"rgbColor\": {\n \"red\": 0.9843137,\n \"green\": 0.7372549,\n \"blue\": 0.015686275\n }\n }\n },\n {\n \"colorType\": \"ACCENT4\",\n \"color\": {\n \"rgbColor\": {\n \"red\": 0.20392157,\n \"green\": 0.65882355,\n \"blue\": 0.3254902\n }\n }\n },\n {\n \"colorType\": \"ACCENT5\",\n \"color\": {\n \"rgbColor\": {\n \"red\": 1,\n \"green\": 0.42745098,\n \"blue\": 0.003921569\n }\n }\n },\n {\n \"colorType\": \"ACCENT6\",\n \"color\": {\n \"rgbColor\": {\n \"red\": 0.27450982,\n \"green\": 0.7411765,\n \"blue\": 0.7764706\n }\n }\n },\n {\n \"colorType\": \"LINK\",\n \"color\": {\n \"rgbColor\": {\n \"red\": 0.06666667,\n \"green\": 0.33333334,\n \"blue\": 0.8\n }\n }\n }\n ]\n }\n },\n \"sheets\": [\n {\n \"properties\": {\n \"sheetId\": 0,\n \"title\": \"Sheet1\",\n \"index\": 0,\n \"sheetType\": \"GRID\",\n \"gridProperties\": {\n \"rowCount\": 1000,\n \"columnCount\": 26\n }\n }\n },\n {\n \"properties\": {\n \"sheetId\": 1865970947,\n \"title\": \"Sheet 2\",\n \"index\": 1,\n \"sheetType\": \"GRID\",\n \"gridProperties\": {\n \"rowCount\": 10,\n \"columnCount\": 10\n }\n }\n }\n ],\n \"spreadsheetUrl\": \"https://docs.google.com/spreadsheets/d/1JDlVaNWQ08apISMjEBgEFJnqzKfRLhqHNbcAxoUfpsE/edit\"\n}\n" + } + } + }, + { + "request": { + "method": "GET", + "uri": "https://sheets.googleapis.com/v4/spreadsheets/1JDlVaNWQ08apISMjEBgEFJnqzKfRLhqHNbcAxoUfpsE/values:batchGet?ranges=%27Sheet+2%27", + "body": null, + "headers": { + "User-Agent": [ + "python-requests/2.34.2" + ], + "Accept-Encoding": [ + "gzip, deflate" + ], + "Accept": [ + "*/*" + ], + "Connection": [ + "keep-alive" + ], + "authorization": [ + "" + ] + } + }, + "response": { + "status": { + "code": 200, + "message": "OK" + }, + "headers": { + "Content-Type": [ + "application/json; charset=UTF-8" + ], + "X-XSS-Protection": [ + "0" + ], + "Vary": [ + "Origin", + "X-Origin", + "Referer" + ], + "Date": [ + "Mon, 15 Jun 2026 13:57:19 GMT" + ], + "Transfer-Encoding": [ + "chunked" + ], + "Alt-Svc": [ + "h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000" + ], + "x-l2-request-path": [ + "l2-managed-6" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "Server": [ + "ESF" + ], + "content-length": [ + "248" + ] + }, + "body": { + "string": "{\n \"spreadsheetId\": \"1JDlVaNWQ08apISMjEBgEFJnqzKfRLhqHNbcAxoUfpsE\",\n \"valueRanges\": [\n {\n \"range\": \"'Sheet 2'!A1:J10\",\n \"majorDimension\": \"ROWS\",\n \"values\": [\n [\n \"1\",\n \"2\"\n ]\n ]\n }\n ]\n}\n" + } + } + }, + { + "request": { + "method": "DELETE", + "uri": "https://www.googleapis.com/drive/v3/files/1JDlVaNWQ08apISMjEBgEFJnqzKfRLhqHNbcAxoUfpsE?supportsAllDrives=True", + "body": null, + "headers": { + "User-Agent": [ + "python-requests/2.34.2" + ], + "Accept-Encoding": [ + "gzip, deflate" + ], + "Accept": [ + "*/*" + ], + "Connection": [ + "keep-alive" + ], + "Content-Length": [ + "0" + ], + "authorization": [ + "" + ] + } + }, + "response": { + "status": { + "code": 204, + "message": "No Content" + }, + "headers": { + "Expires": [ + "Mon, 01 Jan 1990 00:00:00 GMT" + ], + "Content-Type": [ + "text/html" + ], + "Cache-Control": [ + "no-cache, no-store, max-age=0, must-revalidate" + ], + "X-XSS-Protection": [ + "0" + ], + "Vary": [ + "Origin, X-Origin" + ], + "Date": [ + "Mon, 15 Jun 2026 13:57:19 GMT" + ], + "Alt-Svc": [ + "h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000" + ], + "Pragma": [ + "no-cache" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "Content-Length": [ + "0" + ], + "Server": [ + "ESF" + ] + }, + "body": { + "string": "" + } + } + } + ] +} diff --git a/tests/spreadsheet_test.py b/tests/spreadsheet_test.py index ca1b5ed7..dc253f2d 100644 --- a/tests/spreadsheet_test.py +++ b/tests/spreadsheet_test.py @@ -58,6 +58,25 @@ def test_worksheet(self): sheet = self.spreadsheet.worksheet(sheet_title) self.assertIsInstance(sheet, gspread.Worksheet) + @pytest.mark.vcr() + def test_get_all_worksheet_values(self): + sheet1 = self.spreadsheet.sheet1 + sheet1.update([["a", "b"], ["c", "d"]], "A1:B2") + self.spreadsheet.add_worksheet(title="Sheet 2", rows=10, cols=10).update( + [["1", "2"]], "A1:B1" + ) + result = self.spreadsheet.get_all_worksheet_values() + skip_result = self.spreadsheet.get_all_worksheet_values( + skip_worksheet_titles=["Sheet1"] + ) + self.assertIsInstance(result, dict) + self.assertEqual(list(result.keys()), ["Sheet1", "Sheet 2"]) + self.assertEqual(result["Sheet1"], [["a", "b"], ["c", "d"]]) + self.assertEqual(result["Sheet 2"], [["1", "2"]]) + self.assertIsInstance(skip_result, dict) + self.assertEqual(list(skip_result.keys()), ["Sheet 2"]) + self.assertEqual(skip_result["Sheet 2"], [["1", "2"]]) + @pytest.mark.vcr() def test_worksheets(self): n_worksheets_before = len(self.spreadsheet.worksheets()) diff --git a/tests/utils_test.py b/tests/utils_test.py index f64b759a..ca96a973 100644 --- a/tests/utils_test.py +++ b/tests/utils_test.py @@ -49,7 +49,7 @@ def test_addr_converters(self): for row in range(1, 257): for col in range(1, 512): addr = utils.rowcol_to_a1(row, col) - (r, c) = utils.a1_to_rowcol(addr) + r, c = utils.a1_to_rowcol(addr) self.assertEqual((row, col), (r, c)) def test_get_gid(self): @@ -457,6 +457,23 @@ def test_is_full_a1_notation(self): self.assertFalse(utils.is_full_a1_notation("1")) self.assertFalse(utils.is_full_a1_notation("")) + def test_extract_title_from_range(self): + """test extract_title_from_range function""" + # quoted title with spaces + self.assertEqual( + utils.extract_title_from_range("'Sheet Name'!A1:Z100"), "Sheet Name" + ) + # unquoted title + self.assertEqual(utils.extract_title_from_range("Sheet1!A1:Z100"), "Sheet1") + # quoted title without range + self.assertEqual(utils.extract_title_from_range("'Sheet''1'!A1:B2"), "Sheet'1") + self.assertEqual( + utils.extract_title_from_range("'Sheet''''1'!A1:B2"), "Sheet''1" + ) + # invalid input raises exception + with self.assertRaises(gspread.exceptions.InvalidInputValue): + utils.extract_title_from_range("no_exclamation_mark") + def test_get_a1_from_absolute_range(self): """test get_a1_from_absolute_range function""" self.assertEqual(utils.get_a1_from_absolute_range("'Sheet1'!A1:B2"), "A1:B2")