python-google-sheets 1.0.1__py3-none-any.whl → 1.2.0__py3-none-any.whl

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.
@@ -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):
@@ -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,18 +1,18 @@
1
1
  google_sheets/__init__.py,sha256=ZhBNb6oS2tyliSe89Vc0Kw0DSxLMArl3GIMQNf1qsKo,850
2
- google_sheets/api_request.py,sha256=mGkksUBBtundljE6V0FBbRvXV0JoNLiA_8z5ftePsWs,25169
3
- google_sheets/google_sheets.py,sha256=CQZ6n1viNJtxPauxAeSIzVDd2vyPF5uSfziRT84C1ic,7890
2
+ google_sheets/api_request.py,sha256=MJ14qQ8j4LVXxXGl80nhzep4WN76gcyLoZC8QSvy6DE,26470
3
+ google_sheets/google_sheets.py,sha256=uJBdCoFNFSFl-zQIhKm9JT7y3IL8tjvwUXdUNysNpMw,8886
4
4
  google_sheets/styles.py,sha256=9n-4W8EW8F62JxPCL99uctNVv3j703wO2UqoAGWoylo,13418
5
5
  google_sheets/utils.py,sha256=hmqVC75t2Q4lTztKKxerWar7B_HQnrmO2EGhbesdkNU,1695
6
- google_sheets/spreadsheet_requests/__init__.py,sha256=xLJBNONHcrWkwo6nvnHiWdAxhWphnKZ4Z_l7Q0UwtJo,1309
6
+ google_sheets/spreadsheet_requests/__init__.py,sha256=ygiBT-2Rg67DEULY24NcAjFfSUR9IrCedLQz47LjXcs,1398
7
7
  google_sheets/spreadsheet_requests/conditional_format_rule.py,sha256=wCN37BBWnoitXifcl9ni0KlPKxuKEwIVOnt7Z2n6DcE,6775
8
- google_sheets/spreadsheet_requests/dimension.py,sha256=wQyKn2lWI4tFeXDO_da1YcixKqA0JUaPNNJoVmTWd2U,2653
9
- google_sheets/spreadsheet_requests/general_models.py,sha256=22UmtX69hcDVVrMh3enZsSIo-tRymgofPkwZfT-KhK4,2776
8
+ google_sheets/spreadsheet_requests/dimension.py,sha256=CLKyPsEvoQXGCYky_FLU3CBOlAr8qkLVZaKDzng703g,2952
9
+ google_sheets/spreadsheet_requests/general_models.py,sha256=1hq157SuycJ_9bC84lfLajRiL8vkLPMwGc-b-wOBON0,3484
10
10
  google_sheets/spreadsheet_requests/merge_cells.py,sha256=Y7ChGjmb07jCCG1aP2Ws126cq2AXfhF4rwqskvrTv0w,1023
11
11
  google_sheets/spreadsheet_requests/spreadsheet.py,sha256=lUMhrIpo_YOiPEbzgwONeSx4GDaeSn928R83qHcj7F8,3706
12
12
  google_sheets/spreadsheet_requests/update_cells.py,sha256=cOLcDKp4pgj6xgzoWOJXCO8IlmK_htweX-6WzNka9TM,9159
13
13
  google_sheets/spreadsheet_requests/update_sheet_properties.py,sha256=Gvmrc2SV3UCBF40mJCsAQaOKppA_C53dp8LYrRaisfM,1830
14
- python_google_sheets-1.0.1.dist-info/licenses/LICENSE,sha256=2PFWLbALHdUFzFd2QtLQvu7Ku4M6IO53Ns6b892nVvg,1090
15
- python_google_sheets-1.0.1.dist-info/METADATA,sha256=Kp0PNoWM0tr4L1sEkY0KVRgBo7njfs-vuqsh9YhQVvE,15673
16
- python_google_sheets-1.0.1.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
17
- python_google_sheets-1.0.1.dist-info/top_level.txt,sha256=AEiUl4h4VXrqsRO0AiL9C7ArYqmDhVuz--b035K3r34,14
18
- python_google_sheets-1.0.1.dist-info/RECORD,,
14
+ python_google_sheets-1.2.0.dist-info/licenses/LICENSE,sha256=2PFWLbALHdUFzFd2QtLQvu7Ku4M6IO53Ns6b892nVvg,1090
15
+ python_google_sheets-1.2.0.dist-info/METADATA,sha256=eu_uK-kaX_Ygb-wZ3g52w87RYcoiWbR3X4EL9IyOIyo,17740
16
+ python_google_sheets-1.2.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
17
+ python_google_sheets-1.2.0.dist-info/top_level.txt,sha256=AEiUl4h4VXrqsRO0AiL9C7ArYqmDhVuz--b035K3r34,14
18
+ python_google_sheets-1.2.0.dist-info/RECORD,,