Repository: googleapis/google-cloud-python

Issue:
Use `type(o)` instead of `o.__class__` in `ndb`
See https://github.com/googleapis/google-cloud-python/pull/6487#discussion_r233600589


Discussion:
Hmm, I'm not seeing the point here.  `bucket = get_bucket("name")` currently triggers a `GET` request to `/b/name`.  [That API](https://cloud.google.com/storage/docs/json_api/v1/buckets/get) doesn't support any "return all keys" option, so we cannot save the [additional HTTP request](https://cloud.google.com/storage/docs/json_api/v1/objects/list) triggered by `bucket.get_all_keys()`.

Is this complication just for API compatibility with boto?

I think the intent here was to allow people to get all keys and the bucket in one go. (Note that S3's API doesn't support a "get all keys" option either, it's really doing a second HTTP call if validate=True and pulling in all the keys at "get time".)

I don't think this is _super_ important, but the argument could be made for having it... 

It means that code might look like:

``` python
# This does two HTTP requests.
bucket = get_bucket('name', fetch_all_keys=True)  # Or validate=True
print bucket.get_all_keys()  # Ideally fast because it's cached..?
```

Again, not married to this proposal, but worth discussing.

The net time won't be any faster, as the first line will still consume the time for the second HTTP request.

Caching the keys takes us into the rabbit-hole of invalidation:  we would need to add [watch support](https://cloud.google.com/storage/docs/object-change-notification#_Watching) too.

This is definitely not a blocker for a 1.0 release of gcloud.storage

/cc @tseaver 

I made an [argument](https://github.com/GoogleCloudPlatform/gcloud-python/issues/357#issuecomment-72238344) in #357 that this should be closed:

> Yes the "get all keys" / "get all objects" concept is not something that really applies
> to a mature application (one with more than a few thousand objects). As for the
> people "just getting started", the two HTTP requests won't be such a big deal. What's
> more, we shouldn't be encouraging people to get a list of "all objects" locally; they should
> only use what they need (e.g. for serving a request for a signed-in user).

I'm closing it out with that in mind. @jgeewax @tseaver if you disagree please re-open and let's discuss.

I'm fine with closing this out -- but want to clarify that I'm -1 to removing a `get_all_objects` method (or a way to iterate over the keyspace).

Sure thing, we want to keep it around (via `_BlobIterator`).


Resolving patch:
diff --git a/ndb/src/google/cloud/ndb/_datastore_types.py b/ndb/src/google/cloud/ndb/_datastore_types.py
--- a/ndb/src/google/cloud/ndb/_datastore_types.py
+++ b/ndb/src/google/cloud/ndb/_datastore_types.py
@@ -63,7 +63,7 @@ def __init__(self, blob_key):
         elif blob_key is not None:
             raise exceptions.BadValueError(
                 "blob key should be bytes; received "
-                "{} (a {})".format(blob_key, blob_key.__class__.__name__)
+                "{} (a {})".format(blob_key, type(blob_key).__name__)
             )
 
         self._blob_key = blob_key
diff --git a/ndb/src/google/cloud/ndb/model.py b/ndb/src/google/cloud/ndb/model.py
--- a/ndb/src/google/cloud/ndb/model.py
+++ b/ndb/src/google/cloud/ndb/model.py
@@ -149,7 +149,7 @@ def direction(self):
     def __repr__(self):
         """Return a string representation."""
         return "{}(name={!r}, direction={!r})".format(
-            self.__class__.__name__, self.name, self.direction
+            type(self).__name__, self.name, self.direction
         )
 
     def __eq__(self, other):
@@ -196,7 +196,7 @@ def ancestor(self):
     def __repr__(self):
         """Return a string representation."""
         return "{}(kind={!r}, properties={!r}, ancestor={})".format(
-            self.__class__.__name__, self.kind, self.properties, self.ancestor
+            type(self).__name__, self.kind, self.properties, self.ancestor
         )
 
     def __eq__(self, other):
@@ -252,7 +252,7 @@ def id(self):
     def __repr__(self):
         """Return a string representation."""
         return "{}(definition={!r}, state={!r}, id={:d})".format(
-            self.__class__.__name__, self.definition, self.state, self.id
+            type(self).__name__, self.definition, self.state, self.id
         )
 
     def __eq__(self, other):
@@ -658,7 +658,7 @@ def __repr__(self):
         differ from the default values.
         """
         args = []
-        cls = self.__class__
+        cls = type(self)
         for name, is_keyword in self._constructor_info():
             attr = "_{}".format(name)
             instance_val = getattr(self, attr)
@@ -674,7 +674,7 @@ def __repr__(self):
                     as_str = "{}={}".format(name, as_str)
                 args.append(as_str)
 
-        return "{}({})".format(self.__class__.__name__, ", ".join(args))
+        return "{}({})".format(cls.__name__, ", ".join(args))
 
     def _datastore_type(self, value):
         """Internal hook used by property filters.
diff --git a/ndb/src/google/cloud/ndb/query.py b/ndb/src/google/cloud/ndb/query.py
--- a/ndb/src/google/cloud/ndb/query.py
+++ b/ndb/src/google/cloud/ndb/query.py
@@ -105,7 +105,7 @@ def __init__(self, key):
         self._key = key
 
     def __repr__(self):
-        return "{}({!r})".format(self.__class__.__name__, self._key)
+        return "{}({!r})".format(type(self).__name__, self._key)
 
     def __eq__(self, other):
         if not isinstance(other, Parameter):
@@ -435,7 +435,7 @@ def __getnewargs__(self):
 
     def __repr__(self):
         return "{}({!r}, {!r}, {!r})".format(
-            self.__class__.__name__, self._name, self._opsymbol, self._value
+            type(self).__name__, self._name, self._opsymbol, self._value
         )
 
     def __eq__(self, other):
@@ -513,7 +513,7 @@ def __getnewargs__(self):
         return (self.predicate,)
 
     def __repr__(self):
-        return "{}({})".format(self.__class__.__name__, self.predicate)
+        return "{}({})".format(type(self).__name__, self.predicate)
 
     def __eq__(self, other):
         if not isinstance(other, PostFilterNode):


Test patch: