Repository: googleapis/google-cloud-python

Issue:
'ImageAnnotatorClient' object has no attribute 'safe_search_detection'
Hi all,

System is Ubuntu 14.04 and python3.4. google-cloud-vision is installed via pip. Here are the versions:
google-api-core==1.3.0
google-auth==1.5.1
google-cloud-vision==0.33.0
googleapis-common-protos==1.5.3

I am getting 'ImageAnnotatorClient' object has no attribute 'safe_search_detection' from the example below:

```python
from google.cloud import vision
from google.cloud.vision import types

client = vision.ImageAnnotatorClient()
image = types.Image(content=img_content)
response = client.safe_search_detection(image=image)
```

In my local dev environment (MACosx) it is working fine, but in this production setup it is not working...

I have tried reinstalling packages but that did not work either. Somehow, the safe_search_detection() method does not get bound to ImageAnnotatorClient object in this setup.


Discussion:
The `safe_search_detection` method in the [docs](https://googlecloudplatform.github.io/google-cloud-python/latest/vision/gapic/v1/api.html#google.cloud.vision_v1.ImageAnnotatorClient.safe_search_detection) is generated via the `SAFE_SEARCH_DETECTION` feature type.

Can you show how you are installing / reinstalling the code in production?  E.g.:

```bash
$ /path/to/venv/bin/pip install --upgrade google-cloud-vision
```
I use following:
pip3 install google-cloud-vision

I reproduced the issue locally with a fresh install of Python 3.4.3 with using dockerfile and pyenv. When I switch to Python 3.6, and run same commands the issue is not reproduced anymore. Below is the dockerfile I used to switch between Python versions with pyenv:

```
FROM ubuntu:14.04


RUN apt-get update
RUN DEBIAN_FRONTEND=noninteractive apt-get -y install git make build-essential python-dev python-pip libssl-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev curl

RUN git clone git://github.com/yyuu/pyenv.git .pyenv
RUN git clone https://github.com/yyuu/pyenv-virtualenv.git ~/.pyenv/plugins/pyenv-virtualenv

ENV HOME  /
ENV PYENV_ROOT $HOME/.pyenv
ENV PATH $PYENV_ROOT/shims:$PYENV_ROOT/bin:$PATH

RUN pyenv install 3.4.3
#RUN pyenv global 2.7.6

RUN echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.profile
RUN echo '$PYENV_ROOT/shims:$PYENV_ROOT/bin:$PATH"' >> ~/.profile
RUN echo 'eval "$(pyenv virtualenv-init -)"' >> ~/.profile

RUN apt-get -y install vim 

CMD ["/bin/bash", "-l"]
```
With this pyenv, I run pip install google-cloud-vision, then I tried pip install --upgrade pip and then again upgrade google-cloud-vision without success. Then I install Python3.6 in the same system, then run pip install google-cloud-vision and everything works.

Is there any possibility there is a problem with this specific Python 3.4.3 version? 
Thanks for digging further into the problem!

The implementation of the standard library `enum` module in Python 3.4 changed in Python 3.5, and in the (ironically named) `enums34` backward-compatibility library used under Python 2.7, such that the enum type now exposes its members as attributes:  the Python 3.4 version does not.

The introspection code in `google.cloud.vision_helpers` depends on the new implementation.

I have a patch which should make Python 3.4 work, although we are no longer testing by default on 3.4.

Resolving patch:
diff --git a/vision/google/cloud/vision_helpers/__init__.py b/vision/google/cloud/vision_helpers/__init__.py
--- a/vision/google/cloud/vision_helpers/__init__.py
+++ b/vision/google/cloud/vision_helpers/__init__.py
@@ -73,11 +73,6 @@ def _get_all_features(self):
         Returns:
             list: A list of all available features.
         """
-        answer = []
-        for key, value in self.enums.Feature.Type.__dict__.items():
-            if key.upper() != key:
-                continue
-            if not isinstance(value, int) or value == 0:
-                continue
-            answer.append({'type': value})
-        return answer
+        return [
+            {'type': feature}
+            for feature in self.enums.Feature.Type if feature != 0]
diff --git a/vision/google/cloud/vision_helpers/decorators.py b/vision/google/cloud/vision_helpers/decorators.py
--- a/vision/google/cloud/vision_helpers/decorators.py
+++ b/vision/google/cloud/vision_helpers/decorators.py
@@ -26,19 +26,14 @@ def add_single_feature_methods(cls):
     if not hasattr(cls, 'enums'):
         return cls
 
-    # Iterate over the Feature.Type enum and add get a list of
-    # features which will receive single-feature detection methods.
-    features = [k for k in cls.enums.Feature.Type.__dict__.keys()
-                if k.replace('_', '').isalpha() and k.upper() == k]
-
     # Add each single-feature method to the class.
-    for feature in features:
+    for feature in cls.enums.Feature.Type:
         # Sanity check: Do not make a method for the falsy feature.
-        if feature == 'TYPE_UNSPECIFIED':
+        if feature.name == 'TYPE_UNSPECIFIED':
             continue
 
         # Assign the appropriate metadata to the function.
-        detect = _create_single_feature_method(feature, cls.enums.Feature.Type)
+        detect = _create_single_feature_method(feature)
 
         # Assign a qualified name to the function, and perform module
         # replacement on the docstring.
@@ -57,19 +52,18 @@ def add_single_feature_methods(cls):
     return cls
 
 
-def _create_single_feature_method(feature, enum):
+def _create_single_feature_method(feature):
     """Return a function that will detect a single feature.
 
     Args:
-        feature (str): A specific feature defined as an attribute on
+        feature (enum): A specific feature defined as a member of
             :class:`~enums.Feature.Type`.
-        enum (class): The :class:`~enums.Feature.Type` class.
 
     Returns:
         function: A helper function to detect just that feature.
     """
     # Define the function properties.
-    fx_name = feature.lower()
+    fx_name = feature.name.lower()
     if 'detection' in fx_name:
         fx_doc = 'Perform {0}.'.format(fx_name.replace('_', ' '))
     else:
@@ -96,7 +90,7 @@ def _create_single_feature_method(feature, enum):
     """
 
     # Get the actual feature value to send.
-    feature_value = {'type': enum.__dict__[feature]}
+    feature_value = {'type': feature}
 
     # Define the function to be returned.
     def inner(self, image, max_results=None,


Test patch: