Skip to content
This repository has been archived by the owner on May 12, 2020. It is now read-only.

Commit

Permalink
Use the OpenStack coding standards
Browse files Browse the repository at this point in the history
* Use absolute imports
* Fix PEP8, flake8, hacking compliance issues
* Check code with flake 8 on each github push
* Move dev dependencies to requirements-dev.txt
* Update dependencies
* Fix some little bugs with the tests
* Start cleaning up the docblocks
  • Loading branch information
skwashd committed Sep 5, 2016
1 parent e8556ec commit 9d47111
Show file tree
Hide file tree
Showing 42 changed files with 582 additions and 400 deletions.
9 changes: 9 additions & 0 deletions .flake8
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[flake8]
ignore =
# This isn't an OpenStack project so we don't use their headers.
H102,
H103

exclude = .git,__pycache__
# This needs to be dropped to <=10 once AcquiaData.request is refactored.
max_complexity = 16
4 changes: 2 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ sudo: false

# command to install dependencies
install:
- pip install -r requirements.txt
- pip install coveralls
- pip install -r requirements-dev.txt
# command to run tests
script:
- nosetests --with-coverage --cover-package=acapi
- flake8
# Disable sudo http://docs.travis-ci.com/user/migrating-from-legacy/
after_success:
- coveralls
6 changes: 5 additions & 1 deletion acapi/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
"""Wrapper for ACAPI Client."""

from .client import Client
from __future__ import absolute_import

from acapi.client import Client

__all__ = ["Client"]
15 changes: 10 additions & 5 deletions acapi/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@
import os
import requests_cache

from .resources import Site, SiteList, User
from .exceptions import AcquiaCloudException
import acapi.exceptions
from acapi.resources.site import Site
from acapi.resources.sitelist import SiteList
from acapi.resources.user import User

class Client(object):

class Client(object):
"""A Client for accessing the Acquia Cloud API."""

def __init__(
Expand All @@ -33,14 +35,17 @@ def __init__(
if not user or not token:
user, token = self.__find_credentials()
if not user or not token:
raise AcquiaCloudException("Credentials not provided")
msg = "Credentials not provided"
raise acapi.exceptions.AcquiaCloudException(msg)

self.auth = (user, token)
self.realm = realm
self.endpoint = endpoint

if cache is not None:
requests_cache.install_cache(cache_name='acapi', backend='memory', expire_after=cache)
requests_cache.install_cache(cache_name='acapi',
backend='memory',
expire_after=cache)

def generate_uri(self, path):
"""Generate a URI for a ACAPI request.
Expand Down
19 changes: 10 additions & 9 deletions acapi/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,28 +1,28 @@
""" Acquia Cloud API Exceptions. """
"""Acquia Cloud API Exceptions."""

from pprint import pformat

class AcquiaCloudException(Exception):

""" Generic Acquia Cloud API Exception.
class AcquiaCloudException(Exception):
"""Generic Acquia Cloud API Exception.
All ACAPI exceptions should extend this class.
"""

pass

class AcquiaCloudNoDataException(AcquiaCloudException):

""" No data found exception. """
class AcquiaCloudNoDataException(AcquiaCloudException):
"""No data found exception."""

pass

class AcquiaCloudTaskFailedException(AcquiaCloudException):

class AcquiaCloudTaskFailedException(AcquiaCloudException):
"""An Acquia task failure exception."""

def __init__(self, message, task):
""" Constructor.
"""Constructor.
Parameters
----------
Expand All @@ -36,11 +36,12 @@ def __init__(self, message, task):
self.task = task

def __str__(self):
""" Return the string representation of the exception.
"""Return the string representation of the exception.
Returns
-------
str
The error message and pretty printed Task object properties.
"""
return "{msg}\n{task}".format(msg=self.message, task=pformat(self.task, indent=4))
task = pformat(self.task, indent=4)
return "{msg}\n{task}".format(msg=self.message, task=task)
57 changes: 39 additions & 18 deletions acapi/resources/__init__.py
Original file line number Diff line number Diff line change
@@ -1,37 +1,58 @@
""" Imports all the resources. """

# Base classes
from .acquiadata import AcquiaData
from .acquialist import AcquiaList
from .acquiaresource import AcquiaResource
from acapi.resources.acquiadata import AcquiaData
from acapi.resources.acquialist import AcquiaList
from acapi.resources.acquiaresource import AcquiaResource

# Databases can be backed up
from .backup import Backup
from .backuplist import BackupList
from acapi.resources.backup import Backup
from acapi.resources.backuplist import BackupList

# Environments have databases
from .database import Database
from .databaselist import DatabaseList
from acapi.resources.database import Database
from acapi.resources.databaselist import DatabaseList

# Environments have domains
from .domain import Domain
from .domainlist import DomainList
from acapi.resources.domain import Domain
from acapi.resources.domainlist import DomainList

# Sites reference environments
from .environment import Environment
from .environmentlist import EnvironmentList
from acapi.resources.environment import Environment
from acapi.resources.environmentlist import EnvironmentList

# Sites have servers
from .server import Server
from .serverlist import ServerList
from acapi.resources.server import Server
from acapi.resources.serverlist import ServerList

# Sites have tasks
from .task import Task
from .tasklist import TaskList
from acapi.resources.task import Task
from acapi.resources.tasklist import TaskList

# Site is our top level
from .site import Site
from .sitelist import SiteList
from acapi.resources.site import Site
from acapi.resources.sitelist import SiteList

# Users are top level
from .user import User
from acapi.resources.user import User

__all__ = [
'AcquiaData',
'AcquiaList',
'AcquiaResource',
'Backup',
'BackupList',
'Database',
'DatabaseList',
'Domain',
'DomainList',
'Environment',
'EnvironmentList',
'Server',
'ServerList',
'Task',
'TaskList',
'Site',
'SiteList',
'User',
]
37 changes: 24 additions & 13 deletions acapi/resources/acquiadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,23 @@
import requests_cache
import time

from acapi.version import __version__
from platform import python_version
from pprint import pformat
from ..version import __version__

LOGGER = logging.getLogger('acapi.resources.acquiadata')

class AcquiaData(object):

class AcquiaData(object):
"""Acquia Cloud API abstract network resource."""

#: User Agent string
USER_AGENT = 'Acquia Cloud API Client/{mver} (Python {pver})'.format(mver=__version__,
pver=python_version())
RAW_AGENT = 'Acquia Cloud API Client/{mver} (Python {pver})'
USER_AGENT = RAW_AGENT.format(mver=__version__,
pver=python_version())

def __init__(self, uri, auth, data=None):
""" Constructor.
"""Constructor.
Parameters
----------
Expand All @@ -38,7 +39,7 @@ def __init__(self, uri, auth, data=None):
self.last_response = None

def create_task(self, uri, data):
""" Create a new task object from a responses response object.
"""Create a new task object from a responses response object.
Parameters
----------
Expand All @@ -53,15 +54,16 @@ def create_task(self, uri, data):
The Task object.
"""
# We have to do this here to avoid circular dependencies
from .task import Task
from acapi.resources.task import Task
task = Task(uri, self.auth, data=data)
return task

def get_last_response(self):
""" Fetch the last response object. """
"""Fetch the last response object. """
return self.last_response

def request(self, uri=None, method='GET', data=None, params={}, decode_json=True):
def request(self, uri=None, method='GET',
data=None, params={}, decode_json=True):
"""Perform a HTTP requests.
Parameters
Expand All @@ -82,6 +84,7 @@ def request(self, uri=None, method='GET', data=None, params={}, decode_json=True
dict
Decoded JSON response data as a dict object.
"""

self.last_response = None

if None == uri:
Expand All @@ -91,10 +94,12 @@ def request(self, uri=None, method='GET', data=None, params={}, decode_json=True

uri = '{}.json'.format(uri)

resp = None
if 'GET' == method:
attempt = 0
while attempt <= 5:
resp = requests.get(uri, auth=self.auth, headers=headers, params=params)
resp = requests.get(uri, auth=self.auth,
headers=headers, params=params)

if resp.status_code not in list(range(500, 505)):
# No need to retry for if not a server error type.
Expand All @@ -104,20 +109,21 @@ def request(self, uri=None, method='GET', data=None, params={}, decode_json=True
params['acapi_retry'] = attempt
time.sleep((attempt ** 2.0) / 10)


# We need to unset the property or it sticks around.
if 'acapi_retry' in params:
del params['acapi_retry']

if 'POST' == method:
jdata = json.dumps(data)
resp = requests.post(uri, auth=self.auth, headers=headers, params=params, data=jdata)
resp = requests.post(uri, auth=self.auth, headers=headers,
params=params, data=jdata)
# This is a sledgehammer but fine grained invalidation is messy.
if self.is_cache_enabled():
requests_cache.clear()

if 'DELETE' == method:
resp = requests.delete(uri, auth=self.auth, headers=headers, params=params)
resp = requests.delete(uri, auth=self.auth, headers=headers,
params=params)
# Quickest and easiest way to do this.
if self.is_cache_enabled():
requests_cache.clear()
Expand All @@ -141,4 +147,9 @@ def request(self, uri=None, method='GET', data=None, params={}, decode_json=True
return resp.content

def is_cache_enabled(self):
"""Checks if requests cache is enabled.
:return: Cache status.
:rtype: bool.
"""
return hasattr(requests.Session(), 'cache')
28 changes: 14 additions & 14 deletions acapi/resources/acquialist.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
""" Dictionary of Acquia Cloud API resources. """
"""Dictionary of Acquia Cloud API resources."""

from .acquiadata import AcquiaData
from ..exceptions import AcquiaCloudNoDataException
from acapi.exceptions import AcquiaCloudNoDataException
from acapi.resources.acquiadata import AcquiaData

class AcquiaList(AcquiaData, dict):

""" Dictionary of Acquia Cloud API resources. """
class AcquiaList(AcquiaData, dict):
"""Dictionary of Acquia Cloud API resources."""

def __init__(self, base_uri, auth, *args, **kwargs):
""" See :func:AcquiaData.__init__. """
"""See :func:AcquiaData.__init__."""
self.sorted_keys = []

# Setup the dict
Expand All @@ -19,17 +19,17 @@ def __init__(self, base_uri, auth, *args, **kwargs):
AcquiaData.__init__(self, self.uri, auth)

def __delitem__(self, key):
""" See dict.__del_item__(). """
"""See dict.__del_item__()."""
super(AcquiaList, self).__delitem__(key)
self.sorted_keys = []

def __setitem__(self, key, value):
""" See dict.__set_item__(). """
"""See dict.__set_item__()."""
super(AcquiaList, self).__setitem__(key, value)
self.sorted_keys = []

def get_resource_uri(self, res):
""" Generate the resource URI.
"""Generate the resource URI.
Parameters
----------
Expand All @@ -44,7 +44,7 @@ def get_resource_uri(self, res):
return '{base_uri}/{res}'.format(base_uri=self.uri, res=res)

def first(self):
""" Retrieve the first item in the dictionary.
"""Retrieve the first item in the dictionary.
Returns
-------
Expand All @@ -58,15 +58,15 @@ def first(self):
return self[key]

def get_sorted_keys(self):
""" Get a sorted copy of the dictionary keys. """
"""Get a sorted copy of the dictionary keys."""
if not self.sorted_keys:
keys = list(self.keys())
self.sorted_keys = sorted(keys)

return self.sorted_keys

def last(self):
""" Retrieve the last item in the dictionary.
"""Retrieve the last item in the dictionary.
Returns
-------
Expand All @@ -80,7 +80,7 @@ def last(self):
return self[key]

def search_pos(self, pos):
""" Search for a particular key.
"""Search for a particular key.
Parameters
----------
Expand All @@ -96,7 +96,7 @@ def search_pos(self, pos):
return keys[pos]

def set_base_uri(self, base_uri):
""" Set the base URI for the resource.
"""Set the base URI for the resource.
Parameters
----------
Expand Down

0 comments on commit 9d47111

Please sign in to comment.