python-google-sheets 1.0.1__tar.gz → 1.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (23) hide show
  1. {python_google_sheets-1.0.1/python_google_sheets.egg-info → python_google_sheets-1.2.0}/PKG-INFO +66 -22
  2. python_google_sheets-1.0.1/PKG-INFO → python_google_sheets-1.2.0/README.md +59 -38
  3. {python_google_sheets-1.0.1 → python_google_sheets-1.2.0}/google_sheets/api_request.py +45 -10
  4. {python_google_sheets-1.0.1 → python_google_sheets-1.2.0}/google_sheets/google_sheets.py +52 -40
  5. {python_google_sheets-1.0.1 → python_google_sheets-1.2.0}/google_sheets/spreadsheet_requests/__init__.py +4 -0
  6. {python_google_sheets-1.0.1 → python_google_sheets-1.2.0}/google_sheets/spreadsheet_requests/dimension.py +8 -0
  7. {python_google_sheets-1.0.1 → python_google_sheets-1.2.0}/google_sheets/spreadsheet_requests/general_models.py +12 -0
  8. {python_google_sheets-1.0.1 → python_google_sheets-1.2.0}/pyproject.toml +11 -2
  9. python_google_sheets-1.0.1/README.md → python_google_sheets-1.2.0/python_google_sheets.egg-info/PKG-INFO +82 -22
  10. {python_google_sheets-1.0.1 → python_google_sheets-1.2.0}/LICENSE +0 -0
  11. {python_google_sheets-1.0.1 → python_google_sheets-1.2.0}/google_sheets/__init__.py +0 -0
  12. {python_google_sheets-1.0.1 → python_google_sheets-1.2.0}/google_sheets/spreadsheet_requests/conditional_format_rule.py +0 -0
  13. {python_google_sheets-1.0.1 → python_google_sheets-1.2.0}/google_sheets/spreadsheet_requests/merge_cells.py +0 -0
  14. {python_google_sheets-1.0.1 → python_google_sheets-1.2.0}/google_sheets/spreadsheet_requests/spreadsheet.py +0 -0
  15. {python_google_sheets-1.0.1 → python_google_sheets-1.2.0}/google_sheets/spreadsheet_requests/update_cells.py +0 -0
  16. {python_google_sheets-1.0.1 → python_google_sheets-1.2.0}/google_sheets/spreadsheet_requests/update_sheet_properties.py +0 -0
  17. {python_google_sheets-1.0.1 → python_google_sheets-1.2.0}/google_sheets/styles.py +0 -0
  18. {python_google_sheets-1.0.1 → python_google_sheets-1.2.0}/google_sheets/utils.py +0 -0
  19. {python_google_sheets-1.0.1 → python_google_sheets-1.2.0}/python_google_sheets.egg-info/SOURCES.txt +0 -0
  20. {python_google_sheets-1.0.1 → python_google_sheets-1.2.0}/python_google_sheets.egg-info/dependency_links.txt +0 -0
  21. {python_google_sheets-1.0.1 → python_google_sheets-1.2.0}/python_google_sheets.egg-info/requires.txt +0 -0
  22. {python_google_sheets-1.0.1 → python_google_sheets-1.2.0}/python_google_sheets.egg-info/top_level.txt +0 -0
  23. {python_google_sheets-1.0.1 → python_google_sheets-1.2.0}/setup.cfg +0 -0
@@ -1,10 +1,17 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-google-sheets
3
- Version: 1.0.1
3
+ Version: 1.2.0
4
4
  Summary: A lightweight and efficient Python wrapper for the Google Sheets API v4
5
5
  Author-email: Timofey Egorov <timegorr@gmail.com>
6
6
  License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/Timofey28/python-google-sheets
8
+ Project-URL: Issues, https://github.com/Timofey28/python-google-sheets/issues
9
+ Keywords: spreadsheets,google-spreadsheets,google-sheets,api,wrapper,python,pydantic
7
10
  Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Programming Language :: Python :: 3.13
14
+ Classifier: Programming Language :: Python :: 3.14
8
15
  Classifier: Operating System :: OS Independent
9
16
  Requires-Python: >=3.11
10
17
  Description-Content-Type: text/markdown
@@ -81,7 +88,7 @@ spreadsheet_id, url = GoogleSheets.create_spreadsheet(
81
88
  )
82
89
  ```
83
90
 
84
- ### `update_spreadsheet(spreadsheet_id: str, api_requests: list[dict], service: 'Resource') -> None`
91
+ ### `update_spreadsheet(spreadsheet_id: str, api_requests: list[dict], *, service: 'Resource') -> None`
85
92
  Execute a `batchUpdate` with one or more request objects.
86
93
 
87
94
  ```python
@@ -93,7 +100,7 @@ requests = [
93
100
  GoogleSheets.update_spreadsheet(spreadsheet_id, requests, service)
94
101
  ```
95
102
 
96
- ### `copy_sheet(source_spreadsheet_id: str, source_sheet_id: str, destination_spreadsheet_id: str, service: 'Resource') -> SheetProperties`
103
+ ### `copy_sheet(source_spreadsheet_id: str, source_sheet_id: str, destination_spreadsheet_id: str, *, service: 'Resource') -> SheetProperties`
97
104
  Copy a sheet from one spreadsheet into another.
98
105
 
99
106
  ```python
@@ -113,19 +120,39 @@ spreadsheet = GoogleSheets.get_spreadsheet(spreadsheet_id, service)
113
120
  print([sheet.properties.title for sheet in spreadsheet.sheets])
114
121
  ```
115
122
 
116
- ### `get_spreadsheet_range_values(spreadsheet_id: str, sheets: list[str | int], ranges: list[list[str]], service: 'Resource') -> list[list[SimpleType] | list[list[SimpleType]]]`
117
- Read values from multiple ranges across one or more sheets.
123
+ ### `get_spreadsheet_range_values(spreadsheet_id: str, sheets: list[str | int] | str | int, ranges: list[list[str]] | list[str] | str, *, by_columns: bool = False, service: 'Resource') -> list[list[RangeData]] | None:`
124
+ Reads values from multiple ranges across multiple sheets. Returns a matrix of values (RangeData - list[list[SimpleType]]) for each range of each sheet. Returns None if an error occurs.
125
+
126
+ Code from this example returns one range from sheet with id `1601337967` and three ranges from sheet `Summary` (assuming that these sheets exist in the spreadsheet). You can mix and match sheet names and ids as needed.
118
127
 
119
128
  ```python
120
129
  values = GoogleSheets.get_spreadsheet_range_values(
121
- spreadsheet_id=spreadsheet_id,
122
- sheets=[0, 'Summary'],
123
- ranges=[['A2:C8'], ['B2:B12']],
124
- service=service,
130
+ spreadsheet_id=SPREADSHEET_ID,
131
+ sheets=[1601337967, 'Summary'],
132
+ ranges=[['A2:C100'], ['A1:E10', 'F1:F10', 'H1']],
133
+ service=service
125
134
  )
126
- print(values)
127
135
  ```
128
136
 
137
+ Next two examples return the same data (assuming that sheet `Sheet1` exists and has id `0`). With `by_columns=True` the values are grouped by columns instead of rows.
138
+ ```python
139
+ values = GoogleSheets.get_spreadsheet_range_values(
140
+ SPREADSHEET_ID,
141
+ sheets='Sheet1',
142
+ ranges=['A1:E5'],
143
+ by_columns=True,
144
+ service=service
145
+ )
146
+ ```
147
+ ```python
148
+ values = GoogleSheets.get_spreadsheet_range_values(
149
+ SPREADSHEET_ID,
150
+ sheets=0,
151
+ ranges='A1:E5',
152
+ by_columns=True,
153
+ service=service
154
+ )
155
+ ```
129
156
  ---
130
157
 
131
158
  ## `ApiRequest` methods for `batchUpdate`
@@ -151,9 +178,10 @@ req = ApiRequest.update_cells(
151
178
  )
152
179
  ```
153
180
 
181
+ ---
154
182
  ### Conditional formatting
155
183
 
156
- ### `add_boolean_format_rule(*, sheet_id: int, ranges: list[str], condition_type: ConditionType, condition_values: list[ConditionValue] = None, cell_format: CellFormat) -> dict`
184
+ ### `add_boolean_format_rule(sheet_id: int, ranges: list[str], condition_type: ConditionType, *, condition_values: list[ConditionValue] = None, cell_format: CellFormat) -> dict`
157
185
  Add a rule (for example, highlight values greater than `100`).
158
186
 
159
187
  ```python
@@ -171,7 +199,7 @@ req = ApiRequest.add_boolean_format_rule(
171
199
  )
172
200
  ```
173
201
 
174
- ### `GradientRule.add(*, sheet_id: int, ranges: list[str], interpolation_points: tuple[IPTypeAndValue, IPTypeAndValue] | tuple[IPTypeAndValue, IPTypeAndValue, IPTypeAndValue], interpolation_point_colors: tuple[ColorStyle, ColorStyle] | tuple[ColorStyle, ColorStyle, ColorStyle]) -> dict`
202
+ ### `GradientRule.add(sheet_id: int, *, ranges: list[str], interpolation_points: tuple[IPTypeAndValue, IPTypeAndValue] | tuple[IPTypeAndValue, IPTypeAndValue, IPTypeAndValue], interpolation_point_colors: tuple[ColorStyle, ColorStyle] | tuple[ColorStyle, ColorStyle, ColorStyle]) -> dict`
175
203
  Add a custom color scale with 2 or 3 interpolation points.
176
204
 
177
205
  ```python
@@ -193,7 +221,7 @@ req = ApiRequest.GradientRule.add(
193
221
  )
194
222
  ```
195
223
 
196
- ### `GradientRule.add_preset(*, sheet_id: int, ranges: list[str], preset: Preset) -> dict`
224
+ ### `GradientRule.add_preset(sheet_id: int, ranges: list[str], preset: Preset) -> dict`
197
225
  Add a gradient rule from a built-in preset.
198
226
 
199
227
  ```python
@@ -204,14 +232,14 @@ req = ApiRequest.GradientRule.add_preset(
204
232
  )
205
233
  ```
206
234
 
207
- ### `delete_conditional_format_rule(*, sheet_id: int, index: int) -> dict`
235
+ ### `delete_conditional_format_rule(sheet_id: int, *, index: int) -> dict`
208
236
  Delete a conditional format rule by index.
209
237
 
210
238
  ```python
211
239
  req = ApiRequest.delete_conditional_format_rule(sheet_id=0, index=0)
212
240
  ```
213
241
 
214
- ### `update_conditional_format_rule(*, sheet_id: int, index: int, rule: ConditionalFormatRule) -> dict`
242
+ ### `update_conditional_format_rule(sheet_id: int, *, index: int, rule: ConditionalFormatRule) -> dict`
215
243
  Replace an existing conditional format rule by index.
216
244
 
217
245
  ```python
@@ -299,7 +327,7 @@ Delete a sheet.
299
327
  req = ApiRequest.delete_sheet(sheet_id=3)
300
328
  ```
301
329
 
302
- ### `add_sheet(*, sheet_id: int = None, title: str = None, index: int = None, hidden: bool = None, row_count: int = None, column_count: int = None, frozen_row_count: int = None, frozen_column_count: int = None, hide_grid_lines: bool = None) -> dict`
330
+ ### `add_sheet(sheet_id: int = None, *, title: str = None, index: int = None, hidden: bool = None, row_count: int = None, column_count: int = None, frozen_row_count: int = None, frozen_column_count: int = None, hide_grid_lines: bool = None) -> dict`
303
331
  Create a new sheet with optional properties.
304
332
 
305
333
  ```python
@@ -311,6 +339,7 @@ req = ApiRequest.add_sheet(
311
339
  )
312
340
  ```
313
341
 
342
+ ---
314
343
  ### Merge and freeze
315
344
 
316
345
  ### `merge_cells(sheet_id: int, range_: str, merge_type: MergeType = MergeType.MERGE_ALL) -> dict`
@@ -320,7 +349,7 @@ Merge cells in a range.
320
349
  req = ApiRequest.merge_cells(sheet_id=0, range_='A1:C1')
321
350
  ```
322
351
 
323
- ### `unmerge_cells(sheet_id: int, range_: str = None, start_row: int = None, end_row: int = None, start_column: int | str = None, end_column: int | str = None) -> dict`
352
+ ### `unmerge_cells(sheet_id: int, *, range_: str = None, start_row: int = None, end_row: int = None, start_column: int | str = None, end_column: int | str = None) -> dict`
324
353
  Unmerge cells in a range (or by explicit indexes).
325
354
 
326
355
  ```python
@@ -334,24 +363,39 @@ Freeze top rows and/or left columns.
334
363
  req = ApiRequest.freeze(sheet_id=0, rows=1, columns=1)
335
364
  ```
336
365
 
366
+ ---
337
367
  ### Rows and columns
338
368
 
339
- ### `insert_rows(sheet_id: int, start_index: int, end_index: int = None, inherit_from_before: bool = True) -> dict`
340
- Insert one or more rows (zero-based indexes).
369
+ ### `insert_rows(sheet_id: int, start_index: int, end_index: int = None, *, inherit_from_before: bool = True) -> dict`
370
+ Insert one or more rows (Indexes are zero-based and inclusive [start_index, end_index]).
341
371
 
342
372
  ```python
343
373
  req = ApiRequest.insert_rows(sheet_id=0, start_index=5, end_index=9)
344
374
  ```
345
375
 
346
- ### `insert_columns(sheet_id: int, start_index: int, end_index: int = None, inherit_from_before: bool = True) -> dict`
347
- Insert columns (zero-based indexes).
376
+ ### `insert_columns(sheet_id: int, start_index: int, end_index: int = None, *, inherit_from_before: bool = True) -> dict`
377
+ Insert columns (Indexes are zero-based and inclusive [start_index, end_index]).
348
378
 
349
379
  ```python
350
380
  req = ApiRequest.insert_columns(sheet_id=0, start_index=2, end_index=3)
351
381
  ```
352
382
 
383
+ ### `delete_rows(sheet_id: int, start_index: int, end_index: int = None) -> dict`
384
+ Delete one or more rows (Indexes are zero-based and inclusive [start_index, end_index]).
385
+
386
+ ```python
387
+ req = ApiRequest.delete_rows(sheet_id=0, start_index=5, end_index=9)
388
+ ```
389
+
390
+ ### `delete_columns(sheet_id: int, start_index: int, end_index: int = None) -> dict`
391
+ Delete columns (Indexes are zero-based and inclusive [start_index, end_index]).
392
+
393
+ ```python
394
+ req = ApiRequest.delete_columns(sheet_id=0, start_index=2, end_index=3)
395
+ ```
396
+
353
397
  ### `clear_columns(sheet_id: int, rows_count: int, start_index: int, end_index: int = None) -> dict`
354
- Clear values and formatting in one or more columns.
398
+ Clear values and formatting in one or more columns (Indexes are zero-based and inclusive [start_index, end_index]).
355
399
 
356
400
  ```python
357
401
  req = ApiRequest.clear_columns(sheet_id=0, rows_count=500, start_index=4, end_index=5)
@@ -1,19 +1,3 @@
1
- Metadata-Version: 2.4
2
- Name: python-google-sheets
3
- Version: 1.0.1
4
- Summary: A lightweight and efficient Python wrapper for the Google Sheets API v4
5
- Author-email: Timofey Egorov <timegorr@gmail.com>
6
- License-Expression: MIT
7
- Classifier: Programming Language :: Python :: 3
8
- Classifier: Operating System :: OS Independent
9
- Requires-Python: >=3.11
10
- Description-Content-Type: text/markdown
11
- License-File: LICENSE
12
- Requires-Dist: google-auth
13
- Requires-Dist: google-api-python-client
14
- Requires-Dist: pydantic>=2.0.0
15
- Dynamic: license-file
16
-
17
1
  # python-google-sheets
18
2
 
19
3
  A lightweight and efficient Python wrapper for the Google Sheets API v4, leveraging `service account` credentials for seamless authentication.
@@ -81,7 +65,7 @@ spreadsheet_id, url = GoogleSheets.create_spreadsheet(
81
65
  )
82
66
  ```
83
67
 
84
- ### `update_spreadsheet(spreadsheet_id: str, api_requests: list[dict], service: 'Resource') -> None`
68
+ ### `update_spreadsheet(spreadsheet_id: str, api_requests: list[dict], *, service: 'Resource') -> None`
85
69
  Execute a `batchUpdate` with one or more request objects.
86
70
 
87
71
  ```python
@@ -93,7 +77,7 @@ requests = [
93
77
  GoogleSheets.update_spreadsheet(spreadsheet_id, requests, service)
94
78
  ```
95
79
 
96
- ### `copy_sheet(source_spreadsheet_id: str, source_sheet_id: str, destination_spreadsheet_id: str, service: 'Resource') -> SheetProperties`
80
+ ### `copy_sheet(source_spreadsheet_id: str, source_sheet_id: str, destination_spreadsheet_id: str, *, service: 'Resource') -> SheetProperties`
97
81
  Copy a sheet from one spreadsheet into another.
98
82
 
99
83
  ```python
@@ -113,19 +97,39 @@ spreadsheet = GoogleSheets.get_spreadsheet(spreadsheet_id, service)
113
97
  print([sheet.properties.title for sheet in spreadsheet.sheets])
114
98
  ```
115
99
 
116
- ### `get_spreadsheet_range_values(spreadsheet_id: str, sheets: list[str | int], ranges: list[list[str]], service: 'Resource') -> list[list[SimpleType] | list[list[SimpleType]]]`
117
- Read values from multiple ranges across one or more sheets.
100
+ ### `get_spreadsheet_range_values(spreadsheet_id: str, sheets: list[str | int] | str | int, ranges: list[list[str]] | list[str] | str, *, by_columns: bool = False, service: 'Resource') -> list[list[RangeData]] | None:`
101
+ Reads values from multiple ranges across multiple sheets. Returns a matrix of values (RangeData - list[list[SimpleType]]) for each range of each sheet. Returns None if an error occurs.
102
+
103
+ Code from this example returns one range from sheet with id `1601337967` and three ranges from sheet `Summary` (assuming that these sheets exist in the spreadsheet). You can mix and match sheet names and ids as needed.
118
104
 
119
105
  ```python
120
106
  values = GoogleSheets.get_spreadsheet_range_values(
121
- spreadsheet_id=spreadsheet_id,
122
- sheets=[0, 'Summary'],
123
- ranges=[['A2:C8'], ['B2:B12']],
124
- service=service,
107
+ spreadsheet_id=SPREADSHEET_ID,
108
+ sheets=[1601337967, 'Summary'],
109
+ ranges=[['A2:C100'], ['A1:E10', 'F1:F10', 'H1']],
110
+ service=service
125
111
  )
126
- print(values)
127
112
  ```
128
113
 
114
+ Next two examples return the same data (assuming that sheet `Sheet1` exists and has id `0`). With `by_columns=True` the values are grouped by columns instead of rows.
115
+ ```python
116
+ values = GoogleSheets.get_spreadsheet_range_values(
117
+ SPREADSHEET_ID,
118
+ sheets='Sheet1',
119
+ ranges=['A1:E5'],
120
+ by_columns=True,
121
+ service=service
122
+ )
123
+ ```
124
+ ```python
125
+ values = GoogleSheets.get_spreadsheet_range_values(
126
+ SPREADSHEET_ID,
127
+ sheets=0,
128
+ ranges='A1:E5',
129
+ by_columns=True,
130
+ service=service
131
+ )
132
+ ```
129
133
  ---
130
134
 
131
135
  ## `ApiRequest` methods for `batchUpdate`
@@ -151,9 +155,10 @@ req = ApiRequest.update_cells(
151
155
  )
152
156
  ```
153
157
 
158
+ ---
154
159
  ### Conditional formatting
155
160
 
156
- ### `add_boolean_format_rule(*, sheet_id: int, ranges: list[str], condition_type: ConditionType, condition_values: list[ConditionValue] = None, cell_format: CellFormat) -> dict`
161
+ ### `add_boolean_format_rule(sheet_id: int, ranges: list[str], condition_type: ConditionType, *, condition_values: list[ConditionValue] = None, cell_format: CellFormat) -> dict`
157
162
  Add a rule (for example, highlight values greater than `100`).
158
163
 
159
164
  ```python
@@ -171,7 +176,7 @@ req = ApiRequest.add_boolean_format_rule(
171
176
  )
172
177
  ```
173
178
 
174
- ### `GradientRule.add(*, sheet_id: int, ranges: list[str], interpolation_points: tuple[IPTypeAndValue, IPTypeAndValue] | tuple[IPTypeAndValue, IPTypeAndValue, IPTypeAndValue], interpolation_point_colors: tuple[ColorStyle, ColorStyle] | tuple[ColorStyle, ColorStyle, ColorStyle]) -> dict`
179
+ ### `GradientRule.add(sheet_id: int, *, ranges: list[str], interpolation_points: tuple[IPTypeAndValue, IPTypeAndValue] | tuple[IPTypeAndValue, IPTypeAndValue, IPTypeAndValue], interpolation_point_colors: tuple[ColorStyle, ColorStyle] | tuple[ColorStyle, ColorStyle, ColorStyle]) -> dict`
175
180
  Add a custom color scale with 2 or 3 interpolation points.
176
181
 
177
182
  ```python
@@ -193,7 +198,7 @@ req = ApiRequest.GradientRule.add(
193
198
  )
194
199
  ```
195
200
 
196
- ### `GradientRule.add_preset(*, sheet_id: int, ranges: list[str], preset: Preset) -> dict`
201
+ ### `GradientRule.add_preset(sheet_id: int, ranges: list[str], preset: Preset) -> dict`
197
202
  Add a gradient rule from a built-in preset.
198
203
 
199
204
  ```python
@@ -204,14 +209,14 @@ req = ApiRequest.GradientRule.add_preset(
204
209
  )
205
210
  ```
206
211
 
207
- ### `delete_conditional_format_rule(*, sheet_id: int, index: int) -> dict`
212
+ ### `delete_conditional_format_rule(sheet_id: int, *, index: int) -> dict`
208
213
  Delete a conditional format rule by index.
209
214
 
210
215
  ```python
211
216
  req = ApiRequest.delete_conditional_format_rule(sheet_id=0, index=0)
212
217
  ```
213
218
 
214
- ### `update_conditional_format_rule(*, sheet_id: int, index: int, rule: ConditionalFormatRule) -> dict`
219
+ ### `update_conditional_format_rule(sheet_id: int, *, index: int, rule: ConditionalFormatRule) -> dict`
215
220
  Replace an existing conditional format rule by index.
216
221
 
217
222
  ```python
@@ -299,7 +304,7 @@ Delete a sheet.
299
304
  req = ApiRequest.delete_sheet(sheet_id=3)
300
305
  ```
301
306
 
302
- ### `add_sheet(*, sheet_id: int = None, title: str = None, index: int = None, hidden: bool = None, row_count: int = None, column_count: int = None, frozen_row_count: int = None, frozen_column_count: int = None, hide_grid_lines: bool = None) -> dict`
307
+ ### `add_sheet(sheet_id: int = None, *, title: str = None, index: int = None, hidden: bool = None, row_count: int = None, column_count: int = None, frozen_row_count: int = None, frozen_column_count: int = None, hide_grid_lines: bool = None) -> dict`
303
308
  Create a new sheet with optional properties.
304
309
 
305
310
  ```python
@@ -311,6 +316,7 @@ req = ApiRequest.add_sheet(
311
316
  )
312
317
  ```
313
318
 
319
+ ---
314
320
  ### Merge and freeze
315
321
 
316
322
  ### `merge_cells(sheet_id: int, range_: str, merge_type: MergeType = MergeType.MERGE_ALL) -> dict`
@@ -320,7 +326,7 @@ Merge cells in a range.
320
326
  req = ApiRequest.merge_cells(sheet_id=0, range_='A1:C1')
321
327
  ```
322
328
 
323
- ### `unmerge_cells(sheet_id: int, range_: str = None, start_row: int = None, end_row: int = None, start_column: int | str = None, end_column: int | str = None) -> dict`
329
+ ### `unmerge_cells(sheet_id: int, *, range_: str = None, start_row: int = None, end_row: int = None, start_column: int | str = None, end_column: int | str = None) -> dict`
324
330
  Unmerge cells in a range (or by explicit indexes).
325
331
 
326
332
  ```python
@@ -334,24 +340,39 @@ Freeze top rows and/or left columns.
334
340
  req = ApiRequest.freeze(sheet_id=0, rows=1, columns=1)
335
341
  ```
336
342
 
343
+ ---
337
344
  ### Rows and columns
338
345
 
339
- ### `insert_rows(sheet_id: int, start_index: int, end_index: int = None, inherit_from_before: bool = True) -> dict`
340
- Insert one or more rows (zero-based indexes).
346
+ ### `insert_rows(sheet_id: int, start_index: int, end_index: int = None, *, inherit_from_before: bool = True) -> dict`
347
+ Insert one or more rows (Indexes are zero-based and inclusive [start_index, end_index]).
341
348
 
342
349
  ```python
343
350
  req = ApiRequest.insert_rows(sheet_id=0, start_index=5, end_index=9)
344
351
  ```
345
352
 
346
- ### `insert_columns(sheet_id: int, start_index: int, end_index: int = None, inherit_from_before: bool = True) -> dict`
347
- Insert columns (zero-based indexes).
353
+ ### `insert_columns(sheet_id: int, start_index: int, end_index: int = None, *, inherit_from_before: bool = True) -> dict`
354
+ Insert columns (Indexes are zero-based and inclusive [start_index, end_index]).
348
355
 
349
356
  ```python
350
357
  req = ApiRequest.insert_columns(sheet_id=0, start_index=2, end_index=3)
351
358
  ```
352
359
 
360
+ ### `delete_rows(sheet_id: int, start_index: int, end_index: int = None) -> dict`
361
+ Delete one or more rows (Indexes are zero-based and inclusive [start_index, end_index]).
362
+
363
+ ```python
364
+ req = ApiRequest.delete_rows(sheet_id=0, start_index=5, end_index=9)
365
+ ```
366
+
367
+ ### `delete_columns(sheet_id: int, start_index: int, end_index: int = None) -> dict`
368
+ Delete columns (Indexes are zero-based and inclusive [start_index, end_index]).
369
+
370
+ ```python
371
+ req = ApiRequest.delete_columns(sheet_id=0, start_index=2, end_index=3)
372
+ ```
373
+
353
374
  ### `clear_columns(sheet_id: int, rows_count: int, start_index: int, end_index: int = None) -> dict`
354
- Clear values and formatting in one or more columns.
375
+ Clear values and formatting in one or more columns (Indexes are zero-based and inclusive [start_index, end_index]).
355
376
 
356
377
  ```python
357
378
  req = ApiRequest.clear_columns(sheet_id=0, rows_count=500, start_index=4, end_index=5)
@@ -488,4 +509,4 @@ GoogleSheets.update_spreadsheet(spreadsheet_id, requests, service)
488
509
  If you have any questions, encounter issues, or want to suggest improvements:
489
510
 
490
511
  - **Bug Reports & Feature Requests:** Please use [GitHub Issues](https://github.com/Timofey28/python-google-sheets/issues) to report bugs or submit ideas.
491
- - **Questions:** For general inquiries or help with integration, feel free to reach out via email at [timegorr@gmail.com](mailto:timegorr@gmail.com).
512
+ - **Questions:** For general inquiries or help with integration, feel free to reach out via email at [timegorr@gmail.com](mailto:timegorr@gmail.com).
@@ -29,6 +29,7 @@ from .spreadsheet_requests import (
29
29
 
30
30
  # dimension
31
31
  InsertDimension,
32
+ DeleteDimension,
32
33
  UpdateDimensionProperties,
33
34
  AddDimensionGroup,
34
35
  DeleteDimensionGroup,
@@ -111,10 +112,10 @@ class ApiRequest:
111
112
 
112
113
  @staticmethod
113
114
  def add_boolean_format_rule(
114
- *,
115
115
  sheet_id: int,
116
116
  ranges: list[str],
117
117
  condition_type: ConditionType,
118
+ *,
118
119
  condition_values: list[ConditionValue] = None,
119
120
  cell_format: CellFormat,
120
121
  ) -> dict:
@@ -142,8 +143,8 @@ class ApiRequest:
142
143
 
143
144
  @staticmethod
144
145
  def add(
145
- *,
146
146
  sheet_id: int,
147
+ *,
147
148
  ranges: list[str],
148
149
  interpolation_points: tuple[IPTypeAndValue, IPTypeAndValue] | tuple[IPTypeAndValue, IPTypeAndValue, IPTypeAndValue],
149
150
  interpolation_point_colors: tuple[ColorStyle, ColorStyle] | tuple[ColorStyle, ColorStyle, ColorStyle],
@@ -209,10 +210,11 @@ class ApiRequest:
209
210
  RED_YELLOW_GREEN_PERCENT = 'RED_YELLOW_GREEN_PERCENT'
210
211
 
211
212
  @staticmethod
212
- def add_preset(*, sheet_id: int, ranges: list[str], preset: Preset) -> dict:
213
+ def add_preset(sheet_id: int, ranges: list[str], preset: Preset) -> dict:
213
214
  grid_ranges = [GridRange(sheet_id=sheet_id, **ApiRequest._split_excel_range(range_, return_as_dict=True)) for range_ in ranges]
214
215
  AGP = ApiRequest.GradientRule.Preset
215
216
 
217
+ # Two interpolation points presets
216
218
  if preset in (AGP.WHITE_GREEN, AGP.WHITE_YELLOW, AGP.WHITE_RED, AGP.GREEN_WHITE, AGP.YELLOW_WHITE, AGP.RED_WHITE):
217
219
  if preset == AGP.WHITE_YELLOW:
218
220
  minpoint_color_style, maxpoint_color_style = Color_.Basic.WHITE, Color_.ConditionalFormatting.YELLOW
@@ -241,7 +243,7 @@ class ApiRequest:
241
243
  )
242
244
  )).dict()
243
245
 
244
- else: # three interpolation points
246
+ else: # Three interpolation points presets
245
247
  if preset in (AGP.RED_WHITE_GREEN_PERCENTILE, AGP.RED_WHITE_GREEN_PERCENT):
246
248
  minpoint_cs, midpoint_cs, maxpoint_cs = Color_.ConditionalFormatting.RED, Color_.Basic.WHITE, Color_.ConditionalFormatting.GREEN
247
249
  midpoint_type = InterpolationPointType.PERCENTILE if preset == AGP.RED_WHITE_GREEN_PERCENTILE else InterpolationPointType.PERCENT
@@ -275,11 +277,11 @@ class ApiRequest:
275
277
  )).dict()
276
278
 
277
279
  @staticmethod
278
- def delete_conditional_format_rule(*, sheet_id: int, index: int) -> dict:
280
+ def delete_conditional_format_rule(sheet_id: int, *, index: int) -> dict:
279
281
  return DeleteConditionalFormatRule(sheet_id=sheet_id, index=index).dict()
280
282
 
281
283
  @staticmethod
282
- def update_conditional_format_rule(*, sheet_id: int, index: int, rule: ConditionalFormatRule) -> dict:
284
+ def update_conditional_format_rule(sheet_id: int, *, index: int, rule: ConditionalFormatRule) -> dict:
283
285
  return UpdateConditionalFormatRule(sheet_id=sheet_id, index=index, rule=rule).dict()
284
286
 
285
287
  @staticmethod
@@ -340,6 +342,7 @@ class ApiRequest:
340
342
  @staticmethod
341
343
  def unmerge_cells(
342
344
  sheet_id: int,
345
+ *,
343
346
  range_: str = None,
344
347
  start_row: int = None,
345
348
  end_row: int = None,
@@ -376,7 +379,7 @@ class ApiRequest:
376
379
  ).dict()
377
380
 
378
381
  @staticmethod
379
- def insert_rows(sheet_id: int, start_index: int, end_index: int = None, inherit_from_before: bool = True) -> dict:
382
+ def insert_rows(sheet_id: int, start_index: int, end_index: int = None, *, inherit_from_before: bool = True) -> dict:
380
383
  """
381
384
  Indexes are zero-based and inclusive [start_index, end_index]. If end_index is not specified, then a single
382
385
  row will be inserted at start_index.
@@ -393,7 +396,7 @@ class ApiRequest:
393
396
  ).dict()
394
397
 
395
398
  @staticmethod
396
- def insert_columns(sheet_id: int, start_index: int, end_index: int = None, inherit_from_before: bool = True) -> dict:
399
+ def insert_columns(sheet_id: int, start_index: int, end_index: int = None, *, inherit_from_before: bool = True) -> dict:
397
400
  """
398
401
  Indexes are zero-based and inclusive [start_index, end_index]. If end_index is not specified, then a single
399
402
  column will be inserted at start_index.
@@ -409,6 +412,38 @@ class ApiRequest:
409
412
  inherit_from_before=inherit_from_before
410
413
  ).dict()
411
414
 
415
+ @staticmethod
416
+ def delete_rows(sheet_id: int, start_index: int, end_index: int = None):
417
+ """
418
+ Indexes are zero-based and inclusive [start_index, end_index]. If end_index is not specified, then a single
419
+ row at start_index will be deleted.
420
+ """
421
+ end_index = end_index or start_index
422
+ return DeleteDimension(
423
+ range=DimensionRange(
424
+ sheet_id=sheet_id,
425
+ dimension=Dimension.ROWS,
426
+ start_index=start_index,
427
+ end_index=end_index + 1
428
+ )
429
+ ).dict()
430
+
431
+ @staticmethod
432
+ def delete_columns(sheet_id: int, start_index: int, end_index: int = None):
433
+ """
434
+ Indexes are zero-based and inclusive [start_index, end_index]. If end_index is not specified, then a single
435
+ column at start_index will be deleted.
436
+ """
437
+ end_index = end_index or start_index
438
+ return DeleteDimension(
439
+ range=DimensionRange(
440
+ sheet_id=sheet_id,
441
+ dimension=Dimension.COLUMNS,
442
+ start_index=start_index,
443
+ end_index=end_index + 1
444
+ )
445
+ ).dict()
446
+
412
447
  @staticmethod
413
448
  def clear_columns(sheet_id: int, rows_count: int, start_index: int, end_index: int = None) -> dict:
414
449
  """
@@ -516,8 +551,8 @@ class ApiRequest:
516
551
 
517
552
  @staticmethod
518
553
  def add_sheet(
519
- *,
520
554
  sheet_id: int = None,
555
+ *,
521
556
  title: str = None,
522
557
  index: int = None,
523
558
  hidden: bool = None,
@@ -542,7 +577,7 @@ class ApiRequest:
542
577
  )).dict()
543
578
 
544
579
  @staticmethod
545
- def _split_excel_range(range_: str, return_as_dict: bool = False) -> tuple[int, int, int, int] | dict[str, int]:
580
+ def _split_excel_range(range_: str, *, return_as_dict: bool = False) -> tuple[int, int, int, int] | dict[str, int]:
546
581
  if ':' in range_:
547
582
  match = re.match(r'([A-Z]+)(\d+):([A-Z]+)(\d+)$', range_)
548
583
  if not match:
@@ -4,7 +4,7 @@ from google.oauth2.service_account import Credentials
4
4
  from googleapiclient.discovery import build
5
5
  from googleapiclient.errors import HttpError
6
6
 
7
- from .spreadsheet_requests import Spreadsheet, SheetProperties, SimpleType
7
+ from .spreadsheet_requests import Spreadsheet, SheetProperties, RangeData, Dimension, ValueRenderOption, DateTimeRenderOption
8
8
 
9
9
  if TYPE_CHECKING:
10
10
  from googleapiclient.discovery import Resource # noqa
@@ -85,7 +85,7 @@ class GoogleSheets:
85
85
  return spreadsheet_id, f'https://docs.google.com/spreadsheets/d/{spreadsheet_id}'
86
86
 
87
87
  @staticmethod
88
- def update_spreadsheet(spreadsheet_id: str, api_requests: list[dict], service: 'Resource') -> None:
88
+ def update_spreadsheet(spreadsheet_id: str, api_requests: list[dict], *, service: 'Resource') -> None:
89
89
  """
90
90
  Updates Google Sheet with the specified API requests.
91
91
 
@@ -100,7 +100,7 @@ class GoogleSheets:
100
100
  raise e
101
101
 
102
102
  @staticmethod
103
- def copy_sheet(source_spreadsheet_id: str, source_sheet_id: str, destination_spreadsheet_id: str, service: 'Resource') -> SheetProperties:
103
+ def copy_sheet(source_spreadsheet_id: str, source_sheet_id: str, destination_spreadsheet_id: str, *, service: 'Resource') -> SheetProperties:
104
104
  request = service.spreadsheets().sheets().copyTo(
105
105
  spreadsheetId=source_spreadsheet_id,
106
106
  sheetId=source_sheet_id,
@@ -118,63 +118,75 @@ class GoogleSheets:
118
118
  @staticmethod
119
119
  def get_spreadsheet_range_values(
120
120
  spreadsheet_id: str,
121
- sheets: list[str | int],
122
- ranges: list[list[str]],
121
+ sheets: list[str | int] | str | int,
122
+ ranges: list[list[str]] | list[str] | str,
123
+ *,
124
+ by_columns: bool = False,
123
125
  service: 'Resource'
124
- ) -> list[list[SimpleType] | list[list[SimpleType]]]:
126
+ ) -> list[list[RangeData]] | None:
125
127
  """
126
128
  Reads values from the specified ranges of the table.
127
129
  IMPORTANT: If the last cells in the range are empty, they will be omitted. If all cells are empty, an empty
128
130
  list will be returned for that range. However, leading empty cells are preserved and will appear in
129
- the result
131
+ the result.
130
132
 
131
133
  Args:
132
- spreadsheet_id (str): ID of the table
133
- sheets (list[str | int]): Name or ID of the sheets to read from
134
- ranges (list[list[str]]): List of ranges from each sheet to read in A1 notation
135
- service (googleapiclient.discovery.Resource): Google Sheets service object
134
+ spreadsheet_id (str): ID of the table.
135
+ sheets (list[str | int] | str | int): Name or ID of the sheet(s).
136
+ ranges (list[list[str]] | list[str] | str): Single range or list of ranges from each sheet in A1 notation.
137
+ by_columns (bool, optional): If True, returns data organized by columns instead of rows. Defaults to False.
138
+ service (googleapiclient.discovery.Resource): Google Sheets service object.
136
139
 
137
140
  Returns:
138
- list[list[SimpleType] | list[list[SimpleType]]]: List of values from the specified ranges. Each range
139
- corresponds to an element in the list. If the range is a single row or a single column, a list of values
140
- is returned Otherwise, a list of lists of values is returned.
141
+ list[list[RangeData]] | None: For each range of each sheet, returns a matrix of values (RangeData - list[list[SimpleType]]). Returns None if an error occurs.
141
142
  """
142
- assert len(sheets) == len(ranges), 'sheets and ranges must have the same length'
143
+ assert \
144
+ ( # sheets: list[str | int], ranges: list[list[str]]
145
+ isinstance(sheets, list) and not isinstance(sheets[0], list) and
146
+ isinstance(ranges, list) and isinstance(ranges[0], list) and not isinstance(ranges[0][0], list) and
147
+ len(sheets) == len(ranges)
148
+ ) or ( # sheets: str | int, ranges: list[str] | str
149
+ (isinstance(sheets, str) or isinstance(sheets, int)) and
150
+ (isinstance(ranges, list) and not isinstance(ranges[0], list) or isinstance(ranges, str))
151
+ ), 'sheets and ranges must be either list[str | int] and list[list[str]] respectively and same size, or str | int and list[str] | str respectively'
152
+
153
+ # Normalize sheets and ranges to list[str | int] and list[list[str]] respectively
154
+ if isinstance(ranges, str):
155
+ ranges = [ranges]
156
+ if isinstance(sheets, str) or isinstance(sheets, int):
157
+ sheets = [sheets]
158
+ ranges = [ranges]
143
159
 
144
160
  ranges_processed = []
161
+ ss = None
145
162
  if any(isinstance(sheet, int) for sheet in sheets):
146
163
  ss = GoogleSheets.get_spreadsheet(spreadsheet_id, service)
147
- for i in range(len(sheets)):
148
- if isinstance(sheets[i], int):
149
- try:
150
- sheets[i] = next(sht.properties.title for sht in ss.sheets if sht.properties.sheet_id == sheets[i])
151
- except StopIteration:
152
- return [[]]
153
- ranges_processed.extend([f'{sheets[i]}!{range_}' for range_ in ranges[i]])
164
+ for sheet_id_or_name, sheet_ranges in zip(sheets, ranges):
165
+ if isinstance(sheet_id_or_name, int):
166
+ try:
167
+ sheet_name = next(sht.properties.title for sht in ss.sheets if sht.properties.sheet_id == sheet_id_or_name)
168
+ except StopIteration:
169
+ return None
170
+ else:
171
+ sheet_name = sheet_id_or_name
172
+ ranges_processed.extend([f'{sheet_name}!{range_}' for range_ in sheet_ranges])
154
173
 
155
174
  try:
156
175
  response = service.spreadsheets().values().batchGet(
157
176
  spreadsheetId=spreadsheet_id,
158
177
  ranges=ranges_processed,
159
- valueRenderOption='UNFORMATTED_VALUE',
160
- dateTimeRenderOption='FORMATTED_STRING'
178
+ majorDimension=Dimension.COLUMNS if by_columns else Dimension.ROWS,
179
+ valueRenderOption=ValueRenderOption.UNFORMATTED_VALUE,
180
+ dateTimeRenderOption=DateTimeRenderOption.FORMATTED_STRING
161
181
  ).execute(num_retries=5)
162
- except HttpError as e:
163
- raise e
182
+ except HttpError:
183
+ return None
164
184
  else:
165
185
  result = []
166
- for value_range in response['valueRanges']:
167
- values = value_range.get('values', [])
168
- if not values:
169
- result.append([])
170
- continue
171
-
172
- if len(values) == 1: # If range is a single row
173
- result.append(values[0])
174
- else:
175
- if max([len(row) for row in values]) <= 1: # If range is a single column
176
- result.append([(row[0] if row else '') for row in values])
177
- else:
178
- result.append(values)
179
-
186
+ value_ranges = iter(response.get('valueRanges', []))
187
+ for sheet_no in range(len(ranges)):
188
+ sheet_ranges = []
189
+ for range_no in range(len(ranges[sheet_no])):
190
+ sheet_ranges.append(next(value_ranges).get('values', []))
191
+ result.append(sheet_ranges)
180
192
  return result
@@ -24,6 +24,7 @@ from .merge_cells import (
24
24
  )
25
25
  from .dimension import (
26
26
  InsertDimension,
27
+ DeleteDimension,
27
28
  UpdateDimensionProperties,
28
29
  AddDimensionGroup,
29
30
  DeleteDimensionGroup,
@@ -58,9 +59,12 @@ from .spreadsheet import (
58
59
  )
59
60
 
60
61
  from .general_models import (
62
+ ValueRenderOption,
63
+ DateTimeRenderOption,
61
64
  Color,
62
65
  ColorStyle,
63
66
  GridRange,
64
67
  FieldMask,
65
68
  SimpleType,
69
+ RangeData,
66
70
  )
@@ -51,6 +51,14 @@ class InsertDimension(BaseModel):
51
51
  return {class_name: json.loads(super().json(*args, **kwargs, by_alias=True, exclude_none=True))}
52
52
 
53
53
 
54
+ class DeleteDimension(BaseModel):
55
+ range: DimensionRange
56
+
57
+ def dict(self, *args, **kwargs):
58
+ class_name = self.__class__.__name__[0].lower() + self.__class__.__name__[1:]
59
+ return {class_name: json.loads(super().json(*args, **kwargs, by_alias=True, exclude_none=True))}
60
+
61
+
54
62
  class UpdateDimensionProperties(BaseModel):
55
63
  range: DimensionRange
56
64
  properties: DimensionProperties
@@ -4,6 +4,18 @@ from pydantic import BaseModel, Field, model_validator
4
4
 
5
5
 
6
6
  SimpleType = str | int | float | bool
7
+ RangeData = list[list[SimpleType]]
8
+
9
+
10
+ class ValueRenderOption(StrEnum):
11
+ FORMATTED_VALUE = 'FORMATTED_VALUE' # Calculated & formatted according to the cell's formatting. DEFAULT value.
12
+ UNFORMATTED_VALUE = 'UNFORMATTED_VALUE' # Calculated, not formatted (e.g. 123.45 instead of $123.45).
13
+ FORMULA = 'FORMULA' # The formula as entered in the cell, e.g. '=SUM(A1:B1)'. If the cell contains a formula, this field will start with an '=' sign.
14
+
15
+
16
+ class DateTimeRenderOption(StrEnum):
17
+ SERIAL_NUMBER = 'SERIAL_NUMBER' # A number, where 1 corresponds to December 30, 1899. DEFAULT value.
18
+ FORMATTED_STRING = 'FORMATTED_STRING' # A string, formatted according to the cell's formatting.
7
19
 
8
20
 
9
21
  class ThemeColorType(StrEnum):
@@ -6,11 +6,16 @@ build-backend = "setuptools.build_meta"
6
6
  name = "python-google-sheets"
7
7
  description = "A lightweight and efficient Python wrapper for the Google Sheets API v4"
8
8
  authors = [{ name = "Timofey Egorov", email = "timegorr@gmail.com" }]
9
- version = "1.0.1"
9
+ version = "1.2.0"
10
10
  readme = "README.md"
11
11
  requires-python = ">=3.11"
12
+ keywords = ["spreadsheets", "google-spreadsheets", "google-sheets", "api", "wrapper", "python", "pydantic"]
12
13
  classifiers = [
13
14
  "Programming Language :: Python :: 3",
15
+ "Programming Language :: Python :: 3.11",
16
+ "Programming Language :: Python :: 3.12",
17
+ "Programming Language :: Python :: 3.13",
18
+ "Programming Language :: Python :: 3.14",
14
19
  "Operating System :: OS Independent",
15
20
  ]
16
21
  dependencies = [
@@ -19,4 +24,8 @@ dependencies = [
19
24
  "pydantic>=2.0.0",
20
25
  ]
21
26
  license = "MIT"
22
- license-files = ["LICEN[CS]E*"]
27
+ license-files = ["LICENSE*"]
28
+
29
+ [project.urls]
30
+ Homepage = "https://github.com/Timofey28/python-google-sheets"
31
+ Issues = "https://github.com/Timofey28/python-google-sheets/issues"
@@ -1,3 +1,26 @@
1
+ Metadata-Version: 2.4
2
+ Name: python-google-sheets
3
+ Version: 1.2.0
4
+ Summary: A lightweight and efficient Python wrapper for the Google Sheets API v4
5
+ Author-email: Timofey Egorov <timegorr@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/Timofey28/python-google-sheets
8
+ Project-URL: Issues, https://github.com/Timofey28/python-google-sheets/issues
9
+ Keywords: spreadsheets,google-spreadsheets,google-sheets,api,wrapper,python,pydantic
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Programming Language :: Python :: 3.13
14
+ Classifier: Programming Language :: Python :: 3.14
15
+ Classifier: Operating System :: OS Independent
16
+ Requires-Python: >=3.11
17
+ Description-Content-Type: text/markdown
18
+ License-File: LICENSE
19
+ Requires-Dist: google-auth
20
+ Requires-Dist: google-api-python-client
21
+ Requires-Dist: pydantic>=2.0.0
22
+ Dynamic: license-file
23
+
1
24
  # python-google-sheets
2
25
 
3
26
  A lightweight and efficient Python wrapper for the Google Sheets API v4, leveraging `service account` credentials for seamless authentication.
@@ -65,7 +88,7 @@ spreadsheet_id, url = GoogleSheets.create_spreadsheet(
65
88
  )
66
89
  ```
67
90
 
68
- ### `update_spreadsheet(spreadsheet_id: str, api_requests: list[dict], service: 'Resource') -> None`
91
+ ### `update_spreadsheet(spreadsheet_id: str, api_requests: list[dict], *, service: 'Resource') -> None`
69
92
  Execute a `batchUpdate` with one or more request objects.
70
93
 
71
94
  ```python
@@ -77,7 +100,7 @@ requests = [
77
100
  GoogleSheets.update_spreadsheet(spreadsheet_id, requests, service)
78
101
  ```
79
102
 
80
- ### `copy_sheet(source_spreadsheet_id: str, source_sheet_id: str, destination_spreadsheet_id: str, service: 'Resource') -> SheetProperties`
103
+ ### `copy_sheet(source_spreadsheet_id: str, source_sheet_id: str, destination_spreadsheet_id: str, *, service: 'Resource') -> SheetProperties`
81
104
  Copy a sheet from one spreadsheet into another.
82
105
 
83
106
  ```python
@@ -97,19 +120,39 @@ spreadsheet = GoogleSheets.get_spreadsheet(spreadsheet_id, service)
97
120
  print([sheet.properties.title for sheet in spreadsheet.sheets])
98
121
  ```
99
122
 
100
- ### `get_spreadsheet_range_values(spreadsheet_id: str, sheets: list[str | int], ranges: list[list[str]], service: 'Resource') -> list[list[SimpleType] | list[list[SimpleType]]]`
101
- Read values from multiple ranges across one or more sheets.
123
+ ### `get_spreadsheet_range_values(spreadsheet_id: str, sheets: list[str | int] | str | int, ranges: list[list[str]] | list[str] | str, *, by_columns: bool = False, service: 'Resource') -> list[list[RangeData]] | None:`
124
+ Reads values from multiple ranges across multiple sheets. Returns a matrix of values (RangeData - list[list[SimpleType]]) for each range of each sheet. Returns None if an error occurs.
125
+
126
+ Code from this example returns one range from sheet with id `1601337967` and three ranges from sheet `Summary` (assuming that these sheets exist in the spreadsheet). You can mix and match sheet names and ids as needed.
102
127
 
103
128
  ```python
104
129
  values = GoogleSheets.get_spreadsheet_range_values(
105
- spreadsheet_id=spreadsheet_id,
106
- sheets=[0, 'Summary'],
107
- ranges=[['A2:C8'], ['B2:B12']],
108
- service=service,
130
+ spreadsheet_id=SPREADSHEET_ID,
131
+ sheets=[1601337967, 'Summary'],
132
+ ranges=[['A2:C100'], ['A1:E10', 'F1:F10', 'H1']],
133
+ service=service
109
134
  )
110
- print(values)
111
135
  ```
112
136
 
137
+ Next two examples return the same data (assuming that sheet `Sheet1` exists and has id `0`). With `by_columns=True` the values are grouped by columns instead of rows.
138
+ ```python
139
+ values = GoogleSheets.get_spreadsheet_range_values(
140
+ SPREADSHEET_ID,
141
+ sheets='Sheet1',
142
+ ranges=['A1:E5'],
143
+ by_columns=True,
144
+ service=service
145
+ )
146
+ ```
147
+ ```python
148
+ values = GoogleSheets.get_spreadsheet_range_values(
149
+ SPREADSHEET_ID,
150
+ sheets=0,
151
+ ranges='A1:E5',
152
+ by_columns=True,
153
+ service=service
154
+ )
155
+ ```
113
156
  ---
114
157
 
115
158
  ## `ApiRequest` methods for `batchUpdate`
@@ -135,9 +178,10 @@ req = ApiRequest.update_cells(
135
178
  )
136
179
  ```
137
180
 
181
+ ---
138
182
  ### Conditional formatting
139
183
 
140
- ### `add_boolean_format_rule(*, sheet_id: int, ranges: list[str], condition_type: ConditionType, condition_values: list[ConditionValue] = None, cell_format: CellFormat) -> dict`
184
+ ### `add_boolean_format_rule(sheet_id: int, ranges: list[str], condition_type: ConditionType, *, condition_values: list[ConditionValue] = None, cell_format: CellFormat) -> dict`
141
185
  Add a rule (for example, highlight values greater than `100`).
142
186
 
143
187
  ```python
@@ -155,7 +199,7 @@ req = ApiRequest.add_boolean_format_rule(
155
199
  )
156
200
  ```
157
201
 
158
- ### `GradientRule.add(*, sheet_id: int, ranges: list[str], interpolation_points: tuple[IPTypeAndValue, IPTypeAndValue] | tuple[IPTypeAndValue, IPTypeAndValue, IPTypeAndValue], interpolation_point_colors: tuple[ColorStyle, ColorStyle] | tuple[ColorStyle, ColorStyle, ColorStyle]) -> dict`
202
+ ### `GradientRule.add(sheet_id: int, *, ranges: list[str], interpolation_points: tuple[IPTypeAndValue, IPTypeAndValue] | tuple[IPTypeAndValue, IPTypeAndValue, IPTypeAndValue], interpolation_point_colors: tuple[ColorStyle, ColorStyle] | tuple[ColorStyle, ColorStyle, ColorStyle]) -> dict`
159
203
  Add a custom color scale with 2 or 3 interpolation points.
160
204
 
161
205
  ```python
@@ -177,7 +221,7 @@ req = ApiRequest.GradientRule.add(
177
221
  )
178
222
  ```
179
223
 
180
- ### `GradientRule.add_preset(*, sheet_id: int, ranges: list[str], preset: Preset) -> dict`
224
+ ### `GradientRule.add_preset(sheet_id: int, ranges: list[str], preset: Preset) -> dict`
181
225
  Add a gradient rule from a built-in preset.
182
226
 
183
227
  ```python
@@ -188,14 +232,14 @@ req = ApiRequest.GradientRule.add_preset(
188
232
  )
189
233
  ```
190
234
 
191
- ### `delete_conditional_format_rule(*, sheet_id: int, index: int) -> dict`
235
+ ### `delete_conditional_format_rule(sheet_id: int, *, index: int) -> dict`
192
236
  Delete a conditional format rule by index.
193
237
 
194
238
  ```python
195
239
  req = ApiRequest.delete_conditional_format_rule(sheet_id=0, index=0)
196
240
  ```
197
241
 
198
- ### `update_conditional_format_rule(*, sheet_id: int, index: int, rule: ConditionalFormatRule) -> dict`
242
+ ### `update_conditional_format_rule(sheet_id: int, *, index: int, rule: ConditionalFormatRule) -> dict`
199
243
  Replace an existing conditional format rule by index.
200
244
 
201
245
  ```python
@@ -283,7 +327,7 @@ Delete a sheet.
283
327
  req = ApiRequest.delete_sheet(sheet_id=3)
284
328
  ```
285
329
 
286
- ### `add_sheet(*, sheet_id: int = None, title: str = None, index: int = None, hidden: bool = None, row_count: int = None, column_count: int = None, frozen_row_count: int = None, frozen_column_count: int = None, hide_grid_lines: bool = None) -> dict`
330
+ ### `add_sheet(sheet_id: int = None, *, title: str = None, index: int = None, hidden: bool = None, row_count: int = None, column_count: int = None, frozen_row_count: int = None, frozen_column_count: int = None, hide_grid_lines: bool = None) -> dict`
287
331
  Create a new sheet with optional properties.
288
332
 
289
333
  ```python
@@ -295,6 +339,7 @@ req = ApiRequest.add_sheet(
295
339
  )
296
340
  ```
297
341
 
342
+ ---
298
343
  ### Merge and freeze
299
344
 
300
345
  ### `merge_cells(sheet_id: int, range_: str, merge_type: MergeType = MergeType.MERGE_ALL) -> dict`
@@ -304,7 +349,7 @@ Merge cells in a range.
304
349
  req = ApiRequest.merge_cells(sheet_id=0, range_='A1:C1')
305
350
  ```
306
351
 
307
- ### `unmerge_cells(sheet_id: int, range_: str = None, start_row: int = None, end_row: int = None, start_column: int | str = None, end_column: int | str = None) -> dict`
352
+ ### `unmerge_cells(sheet_id: int, *, range_: str = None, start_row: int = None, end_row: int = None, start_column: int | str = None, end_column: int | str = None) -> dict`
308
353
  Unmerge cells in a range (or by explicit indexes).
309
354
 
310
355
  ```python
@@ -318,24 +363,39 @@ Freeze top rows and/or left columns.
318
363
  req = ApiRequest.freeze(sheet_id=0, rows=1, columns=1)
319
364
  ```
320
365
 
366
+ ---
321
367
  ### Rows and columns
322
368
 
323
- ### `insert_rows(sheet_id: int, start_index: int, end_index: int = None, inherit_from_before: bool = True) -> dict`
324
- Insert one or more rows (zero-based indexes).
369
+ ### `insert_rows(sheet_id: int, start_index: int, end_index: int = None, *, inherit_from_before: bool = True) -> dict`
370
+ Insert one or more rows (Indexes are zero-based and inclusive [start_index, end_index]).
325
371
 
326
372
  ```python
327
373
  req = ApiRequest.insert_rows(sheet_id=0, start_index=5, end_index=9)
328
374
  ```
329
375
 
330
- ### `insert_columns(sheet_id: int, start_index: int, end_index: int = None, inherit_from_before: bool = True) -> dict`
331
- Insert columns (zero-based indexes).
376
+ ### `insert_columns(sheet_id: int, start_index: int, end_index: int = None, *, inherit_from_before: bool = True) -> dict`
377
+ Insert columns (Indexes are zero-based and inclusive [start_index, end_index]).
332
378
 
333
379
  ```python
334
380
  req = ApiRequest.insert_columns(sheet_id=0, start_index=2, end_index=3)
335
381
  ```
336
382
 
383
+ ### `delete_rows(sheet_id: int, start_index: int, end_index: int = None) -> dict`
384
+ Delete one or more rows (Indexes are zero-based and inclusive [start_index, end_index]).
385
+
386
+ ```python
387
+ req = ApiRequest.delete_rows(sheet_id=0, start_index=5, end_index=9)
388
+ ```
389
+
390
+ ### `delete_columns(sheet_id: int, start_index: int, end_index: int = None) -> dict`
391
+ Delete columns (Indexes are zero-based and inclusive [start_index, end_index]).
392
+
393
+ ```python
394
+ req = ApiRequest.delete_columns(sheet_id=0, start_index=2, end_index=3)
395
+ ```
396
+
337
397
  ### `clear_columns(sheet_id: int, rows_count: int, start_index: int, end_index: int = None) -> dict`
338
- Clear values and formatting in one or more columns.
398
+ Clear values and formatting in one or more columns (Indexes are zero-based and inclusive [start_index, end_index]).
339
399
 
340
400
  ```python
341
401
  req = ApiRequest.clear_columns(sheet_id=0, rows_count=500, start_index=4, end_index=5)
@@ -472,4 +532,4 @@ GoogleSheets.update_spreadsheet(spreadsheet_id, requests, service)
472
532
  If you have any questions, encounter issues, or want to suggest improvements:
473
533
 
474
534
  - **Bug Reports & Feature Requests:** Please use [GitHub Issues](https://github.com/Timofey28/python-google-sheets/issues) to report bugs or submit ideas.
475
- - **Questions:** For general inquiries or help with integration, feel free to reach out via email at [timegorr@gmail.com](mailto:timegorr@gmail.com).
535
+ - **Questions:** For general inquiries or help with integration, feel free to reach out via email at [timegorr@gmail.com](mailto:timegorr@gmail.com).