Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/google/cloud/bigquery/dataset.py: 44%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

505 statements  

1# Copyright 2015 Google LLC 

2# 

3# Licensed under the Apache License, Version 2.0 (the "License"); 

4# you may not use this file except in compliance with the License. 

5# You may obtain a copy of the License at 

6# 

7# http://www.apache.org/licenses/LICENSE-2.0 

8# 

9# Unless required by applicable law or agreed to in writing, software 

10# distributed under the License is distributed on an "AS IS" BASIS, 

11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 

12# See the License for the specific language governing permissions and 

13# limitations under the License. 

14 

15"""Define API Datasets.""" 

16 

17from __future__ import absolute_import 

18 

19import copy 

20 

21import typing 

22from typing import Optional, List, Dict, Any, Union 

23 

24import google.cloud._helpers # type: ignore 

25 

26from google.cloud.bigquery import _helpers 

27from google.cloud.bigquery.model import ModelReference 

28from google.cloud.bigquery.routine import Routine, RoutineReference 

29from google.cloud.bigquery.table import Table, TableReference 

30from google.cloud.bigquery.encryption_configuration import EncryptionConfiguration 

31from google.cloud.bigquery import external_config 

32 

33 

34def _get_table_reference(self, table_id: str) -> TableReference: 

35 """Constructs a TableReference. 

36 

37 Args: 

38 table_id (str): The ID of the table. 

39 

40 Returns: 

41 google.cloud.bigquery.table.TableReference: 

42 A table reference for a table in this dataset. 

43 """ 

44 return TableReference(self, table_id) 

45 

46 

47def _get_model_reference(self, model_id): 

48 """Constructs a ModelReference. 

49 

50 Args: 

51 model_id (str): the ID of the model. 

52 

53 Returns: 

54 google.cloud.bigquery.model.ModelReference: 

55 A ModelReference for a model in this dataset. 

56 """ 

57 return ModelReference.from_api_repr( 

58 {"projectId": self.project, "datasetId": self.dataset_id, "modelId": model_id} 

59 ) 

60 

61 

62def _get_routine_reference(self, routine_id): 

63 """Constructs a RoutineReference. 

64 

65 Args: 

66 routine_id (str): the ID of the routine. 

67 

68 Returns: 

69 google.cloud.bigquery.routine.RoutineReference: 

70 A RoutineReference for a routine in this dataset. 

71 """ 

72 return RoutineReference.from_api_repr( 

73 { 

74 "projectId": self.project, 

75 "datasetId": self.dataset_id, 

76 "routineId": routine_id, 

77 } 

78 ) 

79 

80 

81class DatasetReference(object): 

82 """DatasetReferences are pointers to datasets. 

83 

84 See 

85 https://cloud.google.com/bigquery/docs/reference/rest/v2/datasets#datasetreference 

86 

87 Args: 

88 project (str): The ID of the project 

89 dataset_id (str): The ID of the dataset 

90 

91 Raises: 

92 ValueError: If either argument is not of type ``str``. 

93 """ 

94 

95 def __init__(self, project: str, dataset_id: str): 

96 if not isinstance(project, str): 

97 raise ValueError("Pass a string for project") 

98 if not isinstance(dataset_id, str): 

99 raise ValueError("Pass a string for dataset_id") 

100 self._project = project 

101 self._dataset_id = dataset_id 

102 

103 @property 

104 def project(self): 

105 """str: Project ID of the dataset.""" 

106 return self._project 

107 

108 @property 

109 def dataset_id(self): 

110 """str: Dataset ID.""" 

111 return self._dataset_id 

112 

113 @property 

114 def path(self): 

115 """str: URL path for the dataset based on project and dataset ID.""" 

116 return "/projects/%s/datasets/%s" % (self.project, self.dataset_id) 

117 

118 table = _get_table_reference 

119 

120 model = _get_model_reference 

121 

122 routine = _get_routine_reference 

123 

124 @classmethod 

125 def from_api_repr(cls, resource: dict) -> "DatasetReference": 

126 """Factory: construct a dataset reference given its API representation 

127 

128 Args: 

129 resource (Dict[str, str]): 

130 Dataset reference resource representation returned from the API 

131 

132 Returns: 

133 google.cloud.bigquery.dataset.DatasetReference: 

134 Dataset reference parsed from ``resource``. 

135 """ 

136 project = resource["projectId"] 

137 dataset_id = resource["datasetId"] 

138 return cls(project, dataset_id) 

139 

140 @classmethod 

141 def from_string( 

142 cls, dataset_id: str, default_project: Optional[str] = None 

143 ) -> "DatasetReference": 

144 """Construct a dataset reference from dataset ID string. 

145 

146 Args: 

147 dataset_id (str): 

148 A dataset ID in standard SQL format. If ``default_project`` 

149 is not specified, this must include both the project ID and 

150 the dataset ID, separated by ``.``. 

151 default_project (Optional[str]): 

152 The project ID to use when ``dataset_id`` does not include a 

153 project ID. 

154 

155 Returns: 

156 DatasetReference: 

157 Dataset reference parsed from ``dataset_id``. 

158 

159 Examples: 

160 >>> DatasetReference.from_string('my-project-id.some_dataset') 

161 DatasetReference('my-project-id', 'some_dataset') 

162 

163 Raises: 

164 ValueError: 

165 If ``dataset_id`` is not a fully-qualified dataset ID in 

166 standard SQL format. 

167 """ 

168 output_dataset_id = dataset_id 

169 parts = _helpers._split_id(dataset_id) 

170 

171 if len(parts) == 1: 

172 if default_project is not None: 

173 output_project_id = default_project 

174 else: 

175 raise ValueError( 

176 "When default_project is not set, dataset_id must be a " 

177 "fully-qualified dataset ID in standard SQL format, " 

178 'e.g., "project.dataset_id" got {}'.format(dataset_id) 

179 ) 

180 elif len(parts) == 2: 

181 output_project_id, output_dataset_id = parts 

182 else: 

183 raise ValueError( 

184 "Too many parts in dataset_id. Expected a fully-qualified " 

185 "dataset ID in standard SQL format, " 

186 'e.g. "project.dataset_id", got {}'.format(dataset_id) 

187 ) 

188 

189 return cls(output_project_id, output_dataset_id) 

190 

191 def to_api_repr(self) -> dict: 

192 """Construct the API resource representation of this dataset reference 

193 

194 Returns: 

195 Dict[str, str]: dataset reference represented as an API resource 

196 """ 

197 return {"projectId": self._project, "datasetId": self._dataset_id} 

198 

199 def _key(self): 

200 """A tuple key that uniquely describes this field. 

201 

202 Used to compute this instance's hashcode and evaluate equality. 

203 

204 Returns: 

205 Tuple[str]: The contents of this :class:`.DatasetReference`. 

206 """ 

207 return (self._project, self._dataset_id) 

208 

209 def __eq__(self, other): 

210 if not isinstance(other, DatasetReference): 

211 return NotImplemented 

212 return self._key() == other._key() 

213 

214 def __ne__(self, other): 

215 return not self == other 

216 

217 def __hash__(self): 

218 return hash(self._key()) 

219 

220 def __str__(self): 

221 return f"{self.project}.{self._dataset_id}" 

222 

223 def __repr__(self): 

224 return "DatasetReference{}".format(self._key()) 

225 

226 

227class AccessEntry(object): 

228 """Represents grant of an access role to an entity. 

229 

230 An entry must have exactly one of the allowed 

231 :class:`google.cloud.bigquery.enums.EntityTypes`. If anything but ``view``, ``routine``, 

232 or ``dataset`` are set, a ``role`` is also required. ``role`` is omitted for ``view``, 

233 ``routine``, ``dataset``, because they are always read-only. 

234 

235 See https://cloud.google.com/bigquery/docs/reference/rest/v2/datasets. 

236 

237 Args: 

238 role: 

239 Role granted to the entity. The following string values are 

240 supported: `'READER'`, `'WRITER'`, `'OWNER'`. It may also be 

241 :data:`None` if the ``entity_type`` is ``view``, ``routine``, or ``dataset``. 

242 

243 entity_type: 

244 Type of entity being granted the role. See 

245 :class:`google.cloud.bigquery.enums.EntityTypes` for supported types. 

246 

247 entity_id: 

248 If the ``entity_type`` is not 'view', 'routine', or 'dataset', the 

249 ``entity_id`` is the ``str`` ID of the entity being granted the role. If 

250 the ``entity_type`` is 'view' or 'routine', the ``entity_id`` is a ``dict`` 

251 representing the view or routine from a different dataset to grant access 

252 to in the following format for views:: 

253 

254 { 

255 'projectId': string, 

256 'datasetId': string, 

257 'tableId': string 

258 } 

259 

260 For routines:: 

261 

262 { 

263 'projectId': string, 

264 'datasetId': string, 

265 'routineId': string 

266 } 

267 

268 If the ``entity_type`` is 'dataset', the ``entity_id`` is a ``dict`` that includes 

269 a 'dataset' field with a ``dict`` representing the dataset and a 'target_types' 

270 field with a ``str`` value of the dataset's resource type:: 

271 

272 { 

273 'dataset': { 

274 'projectId': string, 

275 'datasetId': string, 

276 }, 

277 'target_types: 'VIEWS' 

278 } 

279 

280 Raises: 

281 ValueError: 

282 If a ``view``, ``routine``, or ``dataset`` has ``role`` set, or a non ``view``, 

283 non ``routine``, and non ``dataset`` **does not** have a ``role`` set. 

284 

285 Examples: 

286 >>> entry = AccessEntry('OWNER', 'userByEmail', 'user@example.com') 

287 

288 >>> view = { 

289 ... 'projectId': 'my-project', 

290 ... 'datasetId': 'my_dataset', 

291 ... 'tableId': 'my_table' 

292 ... } 

293 >>> entry = AccessEntry(None, 'view', view) 

294 """ 

295 

296 def __init__( 

297 self, 

298 role: Optional[str] = None, 

299 entity_type: Optional[str] = None, 

300 entity_id: Optional[Union[Dict[str, Any], str]] = None, 

301 **kwargs, 

302 ): 

303 self._properties: Dict[str, Any] = {} 

304 if entity_type is not None: 

305 self._properties[entity_type] = entity_id 

306 self._properties["role"] = role 

307 self._entity_type: Optional[str] = entity_type 

308 for prop, val in kwargs.items(): 

309 setattr(self, prop, val) 

310 

311 @property 

312 def role(self) -> Optional[str]: 

313 """The role of the entry.""" 

314 return typing.cast(Optional[str], self._properties.get("role")) 

315 

316 @role.setter 

317 def role(self, value): 

318 self._properties["role"] = value 

319 

320 @property 

321 def dataset(self) -> Optional[DatasetReference]: 

322 """API resource representation of a dataset reference.""" 

323 value = _helpers._get_sub_prop(self._properties, ["dataset", "dataset"]) 

324 return DatasetReference.from_api_repr(value) if value else None 

325 

326 @dataset.setter 

327 def dataset(self, value): 

328 if self.role is not None: 

329 raise ValueError( 

330 "Role must be None for a dataset. Current " "role: %r" % (self.role) 

331 ) 

332 

333 if isinstance(value, str): 

334 value = DatasetReference.from_string(value).to_api_repr() 

335 

336 if isinstance(value, DatasetReference): 

337 value = value.to_api_repr() 

338 

339 if isinstance(value, (Dataset, DatasetListItem)): 

340 value = value.reference.to_api_repr() 

341 

342 _helpers._set_sub_prop(self._properties, ["dataset", "dataset"], value) 

343 _helpers._set_sub_prop( 

344 self._properties, 

345 ["dataset", "targetTypes"], 

346 self._properties.get("targetTypes"), 

347 ) 

348 

349 @property 

350 def dataset_target_types(self) -> Optional[List[str]]: 

351 """Which resources that the dataset in this entry applies to.""" 

352 return typing.cast( 

353 Optional[List[str]], 

354 _helpers._get_sub_prop(self._properties, ["dataset", "targetTypes"]), 

355 ) 

356 

357 @dataset_target_types.setter 

358 def dataset_target_types(self, value): 

359 self._properties.setdefault("dataset", {}) 

360 _helpers._set_sub_prop(self._properties, ["dataset", "targetTypes"], value) 

361 

362 @property 

363 def routine(self) -> Optional[RoutineReference]: 

364 """API resource representation of a routine reference.""" 

365 value = typing.cast(Optional[Dict], self._properties.get("routine")) 

366 return RoutineReference.from_api_repr(value) if value else None 

367 

368 @routine.setter 

369 def routine(self, value): 

370 if self.role is not None: 

371 raise ValueError( 

372 "Role must be None for a routine. Current " "role: %r" % (self.role) 

373 ) 

374 

375 if isinstance(value, str): 

376 value = RoutineReference.from_string(value).to_api_repr() 

377 

378 if isinstance(value, RoutineReference): 

379 value = value.to_api_repr() 

380 

381 if isinstance(value, Routine): 

382 value = value.reference.to_api_repr() 

383 

384 self._properties["routine"] = value 

385 

386 @property 

387 def view(self) -> Optional[TableReference]: 

388 """API resource representation of a view reference.""" 

389 value = typing.cast(Optional[Dict], self._properties.get("view")) 

390 return TableReference.from_api_repr(value) if value else None 

391 

392 @view.setter 

393 def view(self, value): 

394 if self.role is not None: 

395 raise ValueError( 

396 "Role must be None for a view. Current " "role: %r" % (self.role) 

397 ) 

398 

399 if isinstance(value, str): 

400 value = TableReference.from_string(value).to_api_repr() 

401 

402 if isinstance(value, TableReference): 

403 value = value.to_api_repr() 

404 

405 if isinstance(value, Table): 

406 value = value.reference.to_api_repr() 

407 

408 self._properties["view"] = value 

409 

410 @property 

411 def group_by_email(self) -> Optional[str]: 

412 """An email address of a Google Group to grant access to.""" 

413 return typing.cast(Optional[str], self._properties.get("groupByEmail")) 

414 

415 @group_by_email.setter 

416 def group_by_email(self, value): 

417 self._properties["groupByEmail"] = value 

418 

419 @property 

420 def user_by_email(self) -> Optional[str]: 

421 """An email address of a user to grant access to.""" 

422 return typing.cast(Optional[str], self._properties.get("userByEmail")) 

423 

424 @user_by_email.setter 

425 def user_by_email(self, value): 

426 self._properties["userByEmail"] = value 

427 

428 @property 

429 def domain(self) -> Optional[str]: 

430 """A domain to grant access to.""" 

431 return typing.cast(Optional[str], self._properties.get("domain")) 

432 

433 @domain.setter 

434 def domain(self, value): 

435 self._properties["domain"] = value 

436 

437 @property 

438 def special_group(self) -> Optional[str]: 

439 """A special group to grant access to.""" 

440 return typing.cast(Optional[str], self._properties.get("specialGroup")) 

441 

442 @special_group.setter 

443 def special_group(self, value): 

444 self._properties["specialGroup"] = value 

445 

446 @property 

447 def condition(self) -> Optional["Condition"]: 

448 """Optional[Condition]: The IAM condition associated with this entry.""" 

449 value = typing.cast(Dict[str, Any], self._properties.get("condition")) 

450 return Condition.from_api_repr(value) if value else None 

451 

452 @condition.setter 

453 def condition(self, value: Union["Condition", dict, None]): 

454 """Set the IAM condition for this entry.""" 

455 if value is None: 

456 self._properties["condition"] = None 

457 elif isinstance(value, Condition): 

458 self._properties["condition"] = value.to_api_repr() 

459 elif isinstance(value, dict): 

460 self._properties["condition"] = value 

461 else: 

462 raise TypeError("condition must be a Condition object, dict, or None") 

463 

464 @property 

465 def entity_type(self) -> Optional[str]: 

466 """The entity_type of the entry.""" 

467 

468 # The api_repr for an AccessEntry object is expected to be a dict with 

469 # only a few keys. Two keys that may be present are role and condition. 

470 # Any additional key is going to have one of ~eight different names: 

471 # userByEmail, groupByEmail, domain, dataset, specialGroup, view, 

472 # routine, iamMember 

473 

474 # if self._entity_type is None, see if it needs setting 

475 # i.e. is there a key: value pair that should be associated with 

476 # entity_type and entity_id? 

477 if self._entity_type is None: 

478 resource = self._properties.copy() 

479 # we are empyting the dict to get to the last `key: value`` pair 

480 # so we don't keep these first entries 

481 _ = resource.pop("role", None) 

482 _ = resource.pop("condition", None) 

483 

484 try: 

485 # we only need entity_type, because entity_id gets set elsewhere. 

486 entity_type, _ = resource.popitem() 

487 except KeyError: 

488 entity_type = None 

489 

490 self._entity_type = entity_type 

491 

492 return self._entity_type 

493 

494 @property 

495 def entity_id(self) -> Optional[Union[Dict[str, Any], str]]: 

496 """The entity_id of the entry.""" 

497 if self.entity_type: 

498 entity_type = self.entity_type 

499 else: 

500 return None 

501 return typing.cast( 

502 Optional[Union[Dict[str, Any], str]], 

503 self._properties.get(entity_type, None), 

504 ) 

505 

506 def __eq__(self, other): 

507 if not isinstance(other, AccessEntry): 

508 return NotImplemented 

509 return self._key() == other._key() 

510 

511 def __ne__(self, other): 

512 return not self == other 

513 

514 def __repr__(self): 

515 return f"<AccessEntry: role={self.role}, {self.entity_type}={self.entity_id}>" 

516 

517 def _key(self): 

518 """A tuple key that uniquely describes this field. 

519 Used to compute this instance's hashcode and evaluate equality. 

520 Returns: 

521 Tuple: The contents of this :class:`~google.cloud.bigquery.dataset.AccessEntry`. 

522 """ 

523 

524 properties = self._properties.copy() 

525 

526 # Dicts are not hashable. 

527 # Convert condition to a hashable datatype(s) 

528 condition = properties.get("condition") 

529 if isinstance(condition, dict): 

530 condition_key = tuple(sorted(condition.items())) 

531 properties["condition"] = condition_key 

532 

533 prop_tup = tuple(sorted(properties.items())) 

534 return (self.role, self.entity_type, self.entity_id, prop_tup) 

535 

536 def __hash__(self): 

537 return hash(self._key()) 

538 

539 def to_api_repr(self): 

540 """Construct the API resource representation of this access entry 

541 

542 Returns: 

543 Dict[str, object]: Access entry represented as an API resource 

544 """ 

545 resource = copy.deepcopy(self._properties) 

546 return resource 

547 

548 @classmethod 

549 def from_api_repr(cls, resource: dict) -> "AccessEntry": 

550 """Factory: construct an access entry given its API representation 

551 

552 Args: 

553 resource (Dict[str, object]): 

554 Access entry resource representation returned from the API 

555 

556 Returns: 

557 google.cloud.bigquery.dataset.AccessEntry: 

558 Access entry parsed from ``resource``. 

559 """ 

560 

561 access_entry = cls() 

562 access_entry._properties = resource.copy() 

563 return access_entry 

564 

565 

566class Dataset(object): 

567 """Datasets are containers for tables. 

568 

569 See 

570 https://cloud.google.com/bigquery/docs/reference/rest/v2/datasets#resource-dataset 

571 

572 Args: 

573 dataset_ref (Union[google.cloud.bigquery.dataset.DatasetReference, str]): 

574 A pointer to a dataset. If ``dataset_ref`` is a string, it must 

575 include both the project ID and the dataset ID, separated by 

576 ``.``. 

577 

578 Note: 

579 Fields marked as "Output Only" are populated by the server and will only be 

580 available after calling :meth:`google.cloud.bigquery.client.Client.get_dataset`. 

581 """ 

582 

583 _PROPERTY_TO_API_FIELD = { 

584 "access_entries": "access", 

585 "created": "creationTime", 

586 "default_partition_expiration_ms": "defaultPartitionExpirationMs", 

587 "default_table_expiration_ms": "defaultTableExpirationMs", 

588 "friendly_name": "friendlyName", 

589 "default_encryption_configuration": "defaultEncryptionConfiguration", 

590 "is_case_insensitive": "isCaseInsensitive", 

591 "storage_billing_model": "storageBillingModel", 

592 "max_time_travel_hours": "maxTimeTravelHours", 

593 "default_rounding_mode": "defaultRoundingMode", 

594 "resource_tags": "resourceTags", 

595 "external_catalog_dataset_options": "externalCatalogDatasetOptions", 

596 "access_policy_version": "accessPolicyVersion", 

597 } 

598 

599 def __init__(self, dataset_ref) -> None: 

600 if isinstance(dataset_ref, str): 

601 dataset_ref = DatasetReference.from_string(dataset_ref) 

602 self._properties = {"datasetReference": dataset_ref.to_api_repr(), "labels": {}} 

603 

604 @property 

605 def max_time_travel_hours(self): 

606 """ 

607 Optional[int]: Defines the time travel window in hours. The value can 

608 be from 48 to 168 hours (2 to 7 days), and in multiple of 24 hours 

609 (48, 72, 96, 120, 144, 168). 

610 The default value is 168 hours if this is not set. 

611 """ 

612 return self._properties.get("maxTimeTravelHours") 

613 

614 @max_time_travel_hours.setter 

615 def max_time_travel_hours(self, hours): 

616 if not isinstance(hours, int): 

617 raise ValueError(f"max_time_travel_hours must be an integer. Got {hours}") 

618 if hours < 2 * 24 or hours > 7 * 24: 

619 raise ValueError( 

620 "Time Travel Window should be from 48 to 168 hours (2 to 7 days)" 

621 ) 

622 if hours % 24 != 0: 

623 raise ValueError("Time Travel Window should be multiple of 24") 

624 self._properties["maxTimeTravelHours"] = hours 

625 

626 @property 

627 def default_rounding_mode(self): 

628 """Union[str, None]: defaultRoundingMode of the dataset as set by the user 

629 (defaults to :data:`None`). 

630 

631 Set the value to one of ``'ROUND_HALF_AWAY_FROM_ZERO'``, ``'ROUND_HALF_EVEN'``, or 

632 ``'ROUNDING_MODE_UNSPECIFIED'``. 

633 

634 See `default rounding mode 

635 <https://cloud.google.com/bigquery/docs/reference/rest/v2/datasets#Dataset.FIELDS.default_rounding_mode>`_ 

636 in REST API docs and `updating the default rounding model 

637 <https://cloud.google.com/bigquery/docs/updating-datasets#update_rounding_mode>`_ 

638 guide. 

639 

640 Raises: 

641 ValueError: for invalid value types. 

642 """ 

643 return self._properties.get("defaultRoundingMode") 

644 

645 @default_rounding_mode.setter 

646 def default_rounding_mode(self, value): 

647 possible_values = [ 

648 "ROUNDING_MODE_UNSPECIFIED", 

649 "ROUND_HALF_AWAY_FROM_ZERO", 

650 "ROUND_HALF_EVEN", 

651 ] 

652 if not isinstance(value, str) and value is not None: 

653 raise ValueError("Pass a string, or None") 

654 if value is None: 

655 self._properties["defaultRoundingMode"] = "ROUNDING_MODE_UNSPECIFIED" 

656 if value not in possible_values and value is not None: 

657 raise ValueError( 

658 f'rounding mode needs to be one of {",".join(possible_values)}' 

659 ) 

660 if value: 

661 self._properties["defaultRoundingMode"] = value 

662 

663 @property 

664 def project(self): 

665 """str: Project ID of the project bound to the dataset.""" 

666 return self._properties["datasetReference"]["projectId"] 

667 

668 @property 

669 def path(self): 

670 """str: URL path for the dataset based on project and dataset ID.""" 

671 return "/projects/%s/datasets/%s" % (self.project, self.dataset_id) 

672 

673 @property 

674 def access_entries(self): 

675 """List[google.cloud.bigquery.dataset.AccessEntry]: Dataset's access 

676 entries. 

677 

678 ``role`` augments the entity type and must be present **unless** the 

679 entity type is ``view`` or ``routine``. 

680 

681 Raises: 

682 TypeError: If 'value' is not a sequence 

683 ValueError: 

684 If any item in the sequence is not an 

685 :class:`~google.cloud.bigquery.dataset.AccessEntry`. 

686 """ 

687 entries = self._properties.get("access", []) 

688 return [AccessEntry.from_api_repr(entry) for entry in entries] 

689 

690 @access_entries.setter 

691 def access_entries(self, value): 

692 if not all(isinstance(field, AccessEntry) for field in value): 

693 raise ValueError("Values must be AccessEntry instances") 

694 entries = [entry.to_api_repr() for entry in value] 

695 self._properties["access"] = entries 

696 

697 @property 

698 def created(self): 

699 """Union[datetime.datetime, None]: Output only. Datetime at which the dataset was 

700 created (:data:`None` until set from the server). 

701 """ 

702 creation_time = self._properties.get("creationTime") 

703 if creation_time is not None: 

704 # creation_time will be in milliseconds. 

705 return google.cloud._helpers._datetime_from_microseconds( 

706 1000.0 * float(creation_time) 

707 ) 

708 

709 @property 

710 def dataset_id(self): 

711 """str: Dataset ID.""" 

712 return self._properties["datasetReference"]["datasetId"] 

713 

714 @property 

715 def full_dataset_id(self): 

716 """Union[str, None]: Output only. ID for the dataset resource 

717 (:data:`None` until set from the server). 

718 

719 In the format ``project_id:dataset_id``. 

720 """ 

721 return self._properties.get("id") 

722 

723 @property 

724 def reference(self): 

725 """google.cloud.bigquery.dataset.DatasetReference: A reference to this 

726 dataset. 

727 """ 

728 return DatasetReference(self.project, self.dataset_id) 

729 

730 @property 

731 def etag(self): 

732 """Union[str, None]: Output only. ETag for the dataset resource 

733 (:data:`None` until set from the server). 

734 """ 

735 return self._properties.get("etag") 

736 

737 @property 

738 def modified(self): 

739 """Union[datetime.datetime, None]: Output only. Datetime at which the dataset was 

740 last modified (:data:`None` until set from the server). 

741 """ 

742 modified_time = self._properties.get("lastModifiedTime") 

743 if modified_time is not None: 

744 # modified_time will be in milliseconds. 

745 return google.cloud._helpers._datetime_from_microseconds( 

746 1000.0 * float(modified_time) 

747 ) 

748 

749 @property 

750 def self_link(self): 

751 """Union[str, None]: Output only. URL for the dataset resource 

752 (:data:`None` until set from the server). 

753 """ 

754 return self._properties.get("selfLink") 

755 

756 @property 

757 def default_partition_expiration_ms(self): 

758 """Optional[int]: The default partition expiration for all 

759 partitioned tables in the dataset, in milliseconds. 

760 

761 Once this property is set, all newly-created partitioned tables in 

762 the dataset will have an ``time_paritioning.expiration_ms`` property 

763 set to this value, and changing the value will only affect new 

764 tables, not existing ones. The storage in a partition will have an 

765 expiration time of its partition time plus this value. 

766 

767 Setting this property overrides the use of 

768 ``default_table_expiration_ms`` for partitioned tables: only one of 

769 ``default_table_expiration_ms`` and 

770 ``default_partition_expiration_ms`` will be used for any new 

771 partitioned table. If you provide an explicit 

772 ``time_partitioning.expiration_ms`` when creating or updating a 

773 partitioned table, that value takes precedence over the default 

774 partition expiration time indicated by this property. 

775 """ 

776 return _helpers._int_or_none( 

777 self._properties.get("defaultPartitionExpirationMs") 

778 ) 

779 

780 @default_partition_expiration_ms.setter 

781 def default_partition_expiration_ms(self, value): 

782 self._properties["defaultPartitionExpirationMs"] = _helpers._str_or_none(value) 

783 

784 @property 

785 def default_table_expiration_ms(self): 

786 """Union[int, None]: Default expiration time for tables in the dataset 

787 (defaults to :data:`None`). 

788 

789 Raises: 

790 ValueError: For invalid value types. 

791 """ 

792 return _helpers._int_or_none(self._properties.get("defaultTableExpirationMs")) 

793 

794 @default_table_expiration_ms.setter 

795 def default_table_expiration_ms(self, value): 

796 if not isinstance(value, int) and value is not None: 

797 raise ValueError("Pass an integer, or None") 

798 self._properties["defaultTableExpirationMs"] = _helpers._str_or_none(value) 

799 

800 @property 

801 def description(self): 

802 """Optional[str]: Description of the dataset as set by the user 

803 (defaults to :data:`None`). 

804 

805 Raises: 

806 ValueError: for invalid value types. 

807 """ 

808 return self._properties.get("description") 

809 

810 @description.setter 

811 def description(self, value): 

812 if not isinstance(value, str) and value is not None: 

813 raise ValueError("Pass a string, or None") 

814 self._properties["description"] = value 

815 

816 @property 

817 def friendly_name(self): 

818 """Union[str, None]: Title of the dataset as set by the user 

819 (defaults to :data:`None`). 

820 

821 Raises: 

822 ValueError: for invalid value types. 

823 """ 

824 return self._properties.get("friendlyName") 

825 

826 @friendly_name.setter 

827 def friendly_name(self, value): 

828 if not isinstance(value, str) and value is not None: 

829 raise ValueError("Pass a string, or None") 

830 self._properties["friendlyName"] = value 

831 

832 @property 

833 def location(self): 

834 """Union[str, None]: Location in which the dataset is hosted as set by 

835 the user (defaults to :data:`None`). 

836 

837 Raises: 

838 ValueError: for invalid value types. 

839 """ 

840 return self._properties.get("location") 

841 

842 @location.setter 

843 def location(self, value): 

844 if not isinstance(value, str) and value is not None: 

845 raise ValueError("Pass a string, or None") 

846 self._properties["location"] = value 

847 

848 @property 

849 def labels(self): 

850 """Dict[str, str]: Labels for the dataset. 

851 

852 This method always returns a dict. To change a dataset's labels, 

853 modify the dict, then call 

854 :meth:`google.cloud.bigquery.client.Client.update_dataset`. To delete 

855 a label, set its value to :data:`None` before updating. 

856 

857 Raises: 

858 ValueError: for invalid value types. 

859 """ 

860 return self._properties.setdefault("labels", {}) 

861 

862 @labels.setter 

863 def labels(self, value): 

864 if not isinstance(value, dict): 

865 raise ValueError("Pass a dict") 

866 self._properties["labels"] = value 

867 

868 @property 

869 def resource_tags(self): 

870 """Dict[str, str]: Resource tags of the dataset. 

871 

872 Optional. The tags attached to this dataset. Tag keys are globally 

873 unique. Tag key is expected to be in the namespaced format, for 

874 example "123456789012/environment" where 123456789012 is 

875 the ID of the parent organization or project resource for this tag 

876 key. Tag value is expected to be the short name, for example 

877 "Production". 

878 

879 Raises: 

880 ValueError: for invalid value types. 

881 """ 

882 return self._properties.setdefault("resourceTags", {}) 

883 

884 @resource_tags.setter 

885 def resource_tags(self, value): 

886 if not isinstance(value, dict) and value is not None: 

887 raise ValueError("Pass a dict") 

888 self._properties["resourceTags"] = value 

889 

890 @property 

891 def default_encryption_configuration(self): 

892 """google.cloud.bigquery.encryption_configuration.EncryptionConfiguration: Custom 

893 encryption configuration for all tables in the dataset. 

894 

895 Custom encryption configuration (e.g., Cloud KMS keys) or :data:`None` 

896 if using default encryption. 

897 

898 See `protecting data with Cloud KMS keys 

899 <https://cloud.google.com/bigquery/docs/customer-managed-encryption>`_ 

900 in the BigQuery documentation. 

901 """ 

902 prop = self._properties.get("defaultEncryptionConfiguration") 

903 if prop: 

904 prop = EncryptionConfiguration.from_api_repr(prop) 

905 return prop 

906 

907 @default_encryption_configuration.setter 

908 def default_encryption_configuration(self, value): 

909 api_repr = value 

910 if value: 

911 api_repr = value.to_api_repr() 

912 self._properties["defaultEncryptionConfiguration"] = api_repr 

913 

914 @property 

915 def is_case_insensitive(self): 

916 """Optional[bool]: True if the dataset and its table names are case-insensitive, otherwise False. 

917 By default, this is False, which means the dataset and its table names are case-sensitive. 

918 This field does not affect routine references. 

919 

920 Raises: 

921 ValueError: for invalid value types. 

922 """ 

923 return self._properties.get("isCaseInsensitive") or False 

924 

925 @is_case_insensitive.setter 

926 def is_case_insensitive(self, value): 

927 if not isinstance(value, bool) and value is not None: 

928 raise ValueError("Pass a boolean value, or None") 

929 if value is None: 

930 value = False 

931 self._properties["isCaseInsensitive"] = value 

932 

933 @property 

934 def storage_billing_model(self): 

935 """Union[str, None]: StorageBillingModel of the dataset as set by the user 

936 (defaults to :data:`None`). 

937 

938 Set the value to one of ``'LOGICAL'``, ``'PHYSICAL'``, or 

939 ``'STORAGE_BILLING_MODEL_UNSPECIFIED'``. This change takes 24 hours to 

940 take effect and you must wait 14 days before you can change the storage 

941 billing model again. 

942 

943 See `storage billing model 

944 <https://cloud.google.com/bigquery/docs/reference/rest/v2/datasets#Dataset.FIELDS.storage_billing_model>`_ 

945 in REST API docs and `updating the storage billing model 

946 <https://cloud.google.com/bigquery/docs/updating-datasets#update_storage_billing_models>`_ 

947 guide. 

948 

949 Raises: 

950 ValueError: for invalid value types. 

951 """ 

952 return self._properties.get("storageBillingModel") 

953 

954 @storage_billing_model.setter 

955 def storage_billing_model(self, value): 

956 if not isinstance(value, str) and value is not None: 

957 raise ValueError( 

958 "storage_billing_model must be a string (e.g. 'LOGICAL'," 

959 " 'PHYSICAL', 'STORAGE_BILLING_MODEL_UNSPECIFIED'), or None." 

960 f" Got {repr(value)}." 

961 ) 

962 self._properties["storageBillingModel"] = value 

963 

964 @property 

965 def external_catalog_dataset_options(self): 

966 """Options defining open source compatible datasets living in the 

967 BigQuery catalog. Contains metadata of open source database, schema 

968 or namespace represented by the current dataset.""" 

969 

970 prop = _helpers._get_sub_prop( 

971 self._properties, ["externalCatalogDatasetOptions"] 

972 ) 

973 

974 if prop is not None: 

975 prop = external_config.ExternalCatalogDatasetOptions.from_api_repr(prop) 

976 return prop 

977 

978 @external_catalog_dataset_options.setter 

979 def external_catalog_dataset_options(self, value): 

980 value = _helpers._isinstance_or_raise( 

981 value, external_config.ExternalCatalogDatasetOptions, none_allowed=True 

982 ) 

983 self._properties[ 

984 self._PROPERTY_TO_API_FIELD["external_catalog_dataset_options"] 

985 ] = (value.to_api_repr() if value is not None else None) 

986 

987 @property 

988 def access_policy_version(self): 

989 return self._properties.get("accessPolicyVersion") 

990 

991 @access_policy_version.setter 

992 def access_policy_version(self, value): 

993 if not isinstance(value, int) and value is not None: 

994 raise ValueError("Pass an integer, or None") 

995 self._properties["accessPolicyVersion"] = value 

996 

997 @classmethod 

998 def from_string(cls, full_dataset_id: str) -> "Dataset": 

999 """Construct a dataset from fully-qualified dataset ID. 

1000 

1001 Args: 

1002 full_dataset_id (str): 

1003 A fully-qualified dataset ID in standard SQL format. Must 

1004 include both the project ID and the dataset ID, separated by 

1005 ``.``. 

1006 

1007 Returns: 

1008 Dataset: Dataset parsed from ``full_dataset_id``. 

1009 

1010 Examples: 

1011 >>> Dataset.from_string('my-project-id.some_dataset') 

1012 Dataset(DatasetReference('my-project-id', 'some_dataset')) 

1013 

1014 Raises: 

1015 ValueError: 

1016 If ``full_dataset_id`` is not a fully-qualified dataset ID in 

1017 standard SQL format. 

1018 """ 

1019 return cls(DatasetReference.from_string(full_dataset_id)) 

1020 

1021 @classmethod 

1022 def from_api_repr(cls, resource: dict) -> "Dataset": 

1023 """Factory: construct a dataset given its API representation 

1024 

1025 Args: 

1026 resource (Dict[str: object]): 

1027 Dataset resource representation returned from the API 

1028 

1029 Returns: 

1030 google.cloud.bigquery.dataset.Dataset: 

1031 Dataset parsed from ``resource``. 

1032 """ 

1033 if ( 

1034 "datasetReference" not in resource 

1035 or "datasetId" not in resource["datasetReference"] 

1036 ): 

1037 raise KeyError( 

1038 "Resource lacks required identity information:" 

1039 '["datasetReference"]["datasetId"]' 

1040 ) 

1041 project_id = resource["datasetReference"]["projectId"] 

1042 dataset_id = resource["datasetReference"]["datasetId"] 

1043 dataset = cls(DatasetReference(project_id, dataset_id)) 

1044 dataset._properties = copy.deepcopy(resource) 

1045 return dataset 

1046 

1047 def to_api_repr(self) -> dict: 

1048 """Construct the API resource representation of this dataset 

1049 

1050 Returns: 

1051 Dict[str, object]: The dataset represented as an API resource 

1052 """ 

1053 return copy.deepcopy(self._properties) 

1054 

1055 def _build_resource(self, filter_fields): 

1056 """Generate a resource for ``update``.""" 

1057 return _helpers._build_resource_from_properties(self, filter_fields) 

1058 

1059 table = _get_table_reference 

1060 

1061 model = _get_model_reference 

1062 

1063 routine = _get_routine_reference 

1064 

1065 def __repr__(self): 

1066 return "Dataset({})".format(repr(self.reference)) 

1067 

1068 

1069class DatasetListItem(object): 

1070 """A read-only dataset resource from a list operation. 

1071 

1072 For performance reasons, the BigQuery API only includes some of the 

1073 dataset properties when listing datasets. Notably, 

1074 :attr:`~google.cloud.bigquery.dataset.Dataset.access_entries` is missing. 

1075 

1076 For a full list of the properties that the BigQuery API returns, see the 

1077 `REST documentation for datasets.list 

1078 <https://cloud.google.com/bigquery/docs/reference/rest/v2/datasets/list>`_. 

1079 

1080 

1081 Args: 

1082 resource (Dict[str, str]): 

1083 A dataset-like resource object from a dataset list response. A 

1084 ``datasetReference`` property is required. 

1085 

1086 Raises: 

1087 ValueError: 

1088 If ``datasetReference`` or one of its required members is missing 

1089 from ``resource``. 

1090 """ 

1091 

1092 def __init__(self, resource): 

1093 if "datasetReference" not in resource: 

1094 raise ValueError("resource must contain a datasetReference value") 

1095 if "projectId" not in resource["datasetReference"]: 

1096 raise ValueError( 

1097 "resource['datasetReference'] must contain a projectId value" 

1098 ) 

1099 if "datasetId" not in resource["datasetReference"]: 

1100 raise ValueError( 

1101 "resource['datasetReference'] must contain a datasetId value" 

1102 ) 

1103 self._properties = resource 

1104 

1105 @property 

1106 def project(self): 

1107 """str: Project bound to the dataset.""" 

1108 return self._properties["datasetReference"]["projectId"] 

1109 

1110 @property 

1111 def dataset_id(self): 

1112 """str: Dataset ID.""" 

1113 return self._properties["datasetReference"]["datasetId"] 

1114 

1115 @property 

1116 def full_dataset_id(self): 

1117 """Union[str, None]: ID for the dataset resource (:data:`None` until 

1118 set from the server) 

1119 

1120 In the format ``project_id:dataset_id``. 

1121 """ 

1122 return self._properties.get("id") 

1123 

1124 @property 

1125 def friendly_name(self): 

1126 """Union[str, None]: Title of the dataset as set by the user 

1127 (defaults to :data:`None`). 

1128 """ 

1129 return self._properties.get("friendlyName") 

1130 

1131 @property 

1132 def labels(self): 

1133 """Dict[str, str]: Labels for the dataset.""" 

1134 return self._properties.setdefault("labels", {}) 

1135 

1136 @property 

1137 def reference(self): 

1138 """google.cloud.bigquery.dataset.DatasetReference: A reference to this 

1139 dataset. 

1140 """ 

1141 return DatasetReference(self.project, self.dataset_id) 

1142 

1143 table = _get_table_reference 

1144 

1145 model = _get_model_reference 

1146 

1147 routine = _get_routine_reference 

1148 

1149 

1150class Condition(object): 

1151 """Represents a textual expression in the Common Expression Language (CEL) syntax. 

1152 

1153 Typically used for filtering or policy rules, such as in IAM Conditions 

1154 or BigQuery row/column access policies. 

1155 

1156 See: 

1157 https://cloud.google.com/iam/docs/reference/rest/Shared.Types/Expr 

1158 https://github.com/google/cel-spec 

1159 

1160 Args: 

1161 expression (str): 

1162 The condition expression string using CEL syntax. This is required. 

1163 Example: ``resource.type == "compute.googleapis.com/Instance"`` 

1164 title (Optional[str]): 

1165 An optional title for the condition, providing a short summary. 

1166 Example: ``"Request is for a GCE instance"`` 

1167 description (Optional[str]): 

1168 An optional description of the condition, providing a detailed explanation. 

1169 Example: ``"This condition checks whether the resource is a GCE instance."`` 

1170 """ 

1171 

1172 def __init__( 

1173 self, 

1174 expression: str, 

1175 title: Optional[str] = None, 

1176 description: Optional[str] = None, 

1177 ): 

1178 self._properties: Dict[str, Any] = {} 

1179 # Use setters to initialize properties, which also handle validation 

1180 self.expression = expression 

1181 self.title = title 

1182 self.description = description 

1183 

1184 @property 

1185 def title(self) -> Optional[str]: 

1186 """Optional[str]: The title for the condition.""" 

1187 return self._properties.get("title") 

1188 

1189 @title.setter 

1190 def title(self, value: Optional[str]): 

1191 if value is not None and not isinstance(value, str): 

1192 raise ValueError("Pass a string for title, or None") 

1193 self._properties["title"] = value 

1194 

1195 @property 

1196 def description(self) -> Optional[str]: 

1197 """Optional[str]: The description for the condition.""" 

1198 return self._properties.get("description") 

1199 

1200 @description.setter 

1201 def description(self, value: Optional[str]): 

1202 if value is not None and not isinstance(value, str): 

1203 raise ValueError("Pass a string for description, or None") 

1204 self._properties["description"] = value 

1205 

1206 @property 

1207 def expression(self) -> str: 

1208 """str: The expression string for the condition.""" 

1209 

1210 # Cast assumes expression is always set due to __init__ validation 

1211 return typing.cast(str, self._properties.get("expression")) 

1212 

1213 @expression.setter 

1214 def expression(self, value: str): 

1215 if not isinstance(value, str): 

1216 raise ValueError("Pass a non-empty string for expression") 

1217 if not value: 

1218 raise ValueError("expression cannot be an empty string") 

1219 self._properties["expression"] = value 

1220 

1221 def to_api_repr(self) -> Dict[str, Any]: 

1222 """Construct the API resource representation of this Condition.""" 

1223 return self._properties 

1224 

1225 @classmethod 

1226 def from_api_repr(cls, resource: Dict[str, Any]) -> "Condition": 

1227 """Factory: construct a Condition instance given its API representation.""" 

1228 

1229 # Ensure required fields are present in the resource if necessary 

1230 if "expression" not in resource: 

1231 raise ValueError("API representation missing required 'expression' field.") 

1232 

1233 return cls( 

1234 expression=resource["expression"], 

1235 title=resource.get("title"), 

1236 description=resource.get("description"), 

1237 ) 

1238 

1239 def __eq__(self, other: object) -> bool: 

1240 """Check for equality based on expression, title, and description.""" 

1241 if not isinstance(other, Condition): 

1242 return NotImplemented 

1243 return self._key() == other._key() 

1244 

1245 def _key(self): 

1246 """A tuple key that uniquely describes this field. 

1247 Used to compute this instance's hashcode and evaluate equality. 

1248 Returns: 

1249 Tuple: The contents of this :class:`~google.cloud.bigquery.dataset.AccessEntry`. 

1250 """ 

1251 

1252 properties = self._properties.copy() 

1253 

1254 # Dicts are not hashable. 

1255 # Convert object to a hashable datatype(s) 

1256 prop_tup = tuple(sorted(properties.items())) 

1257 return prop_tup 

1258 

1259 def __ne__(self, other: object) -> bool: 

1260 """Check for inequality.""" 

1261 return not self == other 

1262 

1263 def __hash__(self) -> int: 

1264 """Generate a hash based on expression, title, and description.""" 

1265 return hash(self._key()) 

1266 

1267 def __repr__(self) -> str: 

1268 """Return a string representation of the Condition object.""" 

1269 parts = [f"expression={self.expression!r}"] 

1270 if self.title is not None: 

1271 parts.append(f"title={self.title!r}") 

1272 if self.description is not None: 

1273 parts.append(f"description={self.description!r}") 

1274 return f"Condition({', '.join(parts)})"