ValueError: ('Iterator has already started', <google.api_core.page_iterator.HTTPIterator object at 0xaaaaaaa>)

pythonでCloud Storageのファイル数を数えようとしたらこんなエラーがでました。
なんなんだと。

  File "/........./lib64/python3.6/site-packages/google/api_core/page_iterator.py", line 227, in __iter__
    raise ValueError("Iterator has already started", self)
ValueError: ('Iterator has already started', <google.api_core.page_iterator.HTTPIterator object at 0x7fc844c4aeb8>)

そもそものコードはこちらです。
やりたいことは

  • 対象フォルダのファイルを探す。
  • ファイルが無ければ終了。
  • ファイルがあればファイル名を表示する。

というものです。

from google.cloud import storage
import sys

storage_client = storage.Client()
bucket = storage_client.bucket(BUCKET_NAME)

### 集計対象のファイルを探す
blobs = bucket.list_blobs(
  prefix=TARGET_GCS_FOLDER + '/hoge_'
)

if len(list(blobs)) == 0:
  print("Error, No target files....")
  sys.exit(1)

for item in blobs:  # <--- ここ!!!
  print(item.name)

そんでどうやらエラーの箇所はblobオブジェクトをイテレートしてファイル名を表示するところでした。

なぜかと?

どうやらblobオブジェクトのイテレーターは1回しか使えないみたいです。
File "/........./lib64/python3.6/site-packages/google/api_core/page_iterator.py"についてコードを見てみると

class Iterator(object):
...................
    def __init__(
        self,
        client,
        item_to_value=_item_to_value_identity,
        page_token=None,
        max_results=None,
    ):
        self._started = False # <--- ここ!!
        self.client = client
.......................
    def __iter__(self):
        """Iterator for each item returned.

        Returns:
            types.GeneratorType[Any]: A generator of items from the API.

        Raises:
            ValueError: If the iterator has already been started.
        """
        if self._started:
            raise ValueError("Iterator has already started", self)  # <--- ここ!!!
        self._started = True
        return self._items_iter()

とあります。
一度__iter__が呼ばれてしまうとself._startedがTrueになりValueErrorがなってしまうことがわかります。

ここで最初の__iter__ですが

if len(list(blobs)) == 0:

でリスト数を数えています。このlist(...)で__iter__が呼ばれているので、すでにself._startedがTrueとなっていたわけです。

なので一度別のリスト型変数に入れなおしてからやればエラーになりません。

blob_list_obj = list(blobs)

if len(blob_list_obj) == 0:
  print("Error, No target files....")
  sys.exit(1)

for item in blob_list_obj:
  print(item.name)


qiita.com