1010from __future__ import annotations
1111
1212import logging
13+ import os
1314from collections .abc import Iterable
1415from datetime import datetime , timedelta
1516from itertools import chain
1920
2021from b2sdk ._internal .api import B2Api
2122from b2sdk ._internal .bucket import Bucket
22- from b2sdk ._internal .exception import BadRequest , BucketIdNotFound , FileNotPresent , TooManyRequests
23+ from b2sdk ._internal .exception import (
24+ BadRequest ,
25+ BucketIdNotFound ,
26+ DuplicateBucketName ,
27+ FileNotPresent ,
28+ TooManyRequests ,
29+ )
2330from b2sdk ._internal .file_lock import NO_RETENTION_FILE_SETTING , LegalHold , RetentionMode
2431from b2sdk ._internal .testing .helpers .buckets import (
2532 BUCKET_CREATED_AT_MILLIS ,
@@ -48,14 +55,26 @@ def __init__(
4855 self .general_prefix = general_prefix
4956 self .dont_cleanup_old_buckets = dont_cleanup_old_buckets
5057 self .b2_api = b2_api
51- self .bucket_name_log : list [str ] = []
58+ self .bucket_name_mapping : dict [str , str ] = {}
5259
5360 def new_bucket_name (self ) -> str :
54- bucket_name = self .current_run_prefix + bucket_name_part (
55- BUCKET_NAME_LENGTH - len (self .current_run_prefix )
61+ attempts = 5
62+
63+ for _ in range (attempts ):
64+ bucket_name = self .current_run_prefix + bucket_name_part (
65+ BUCKET_NAME_LENGTH - len (self .current_run_prefix )
66+ )
67+ if bucket_name not in self .bucket_name_mapping :
68+ return bucket_name
69+
70+ raise RuntimeError (
71+ 'Failed to generate a unique bucket name after %d attempts. Last tried name: %s. Existing buckets:\n %s'
72+ % (
73+ attempts ,
74+ bucket_name ,
75+ self .bucket_name_mapping ,
76+ )
5677 )
57- self .bucket_name_log .append (bucket_name )
58- return bucket_name
5978
6079 def new_bucket_info (self ) -> dict :
6180 return {
@@ -65,12 +84,25 @@ def new_bucket_info(self) -> dict:
6584
6685 def create_bucket (self , bucket_type : str = 'allPublic' , ** kwargs ) -> Bucket :
6786 bucket_name = kwargs .pop ('name' , self .new_bucket_name ())
68- return self .b2_api .create_bucket (
69- bucket_name ,
70- bucket_type = bucket_type ,
71- bucket_info = self .new_bucket_info (),
72- ** kwargs ,
73- )
87+
88+ try :
89+ bucket = self .b2_api .create_bucket (
90+ bucket_name ,
91+ bucket_type = bucket_type ,
92+ bucket_info = self .new_bucket_info (),
93+ ** kwargs ,
94+ )
95+ except DuplicateBucketName :
96+ logger .error (
97+ 'Bucket %s already exists. Currently used buckets:\n %s' ,
98+ bucket_name ,
99+ self .bucket_name_mapping ,
100+ )
101+ raise
102+
103+ self .bucket_name_mapping [bucket_name ] = os .getenv ('PYTEST_CURRENT_TEST' , 'unknown test' )
104+
105+ return bucket
74106
75107 def _should_remove_bucket (self , bucket : Bucket ) -> tuple [bool , str ]:
76108 if self .current_run_prefix and bucket .name .startswith (self .current_run_prefix ):
0 commit comments