blob: 02c022b1942c4170f01f277056616153e63c5a02 [file] [log] [blame]
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
libcloud linter
This script checks the libcloud codebase for registered drivers that don't
comply to libcloud API guidelines.
"""
import inspect
import os
import libcloud
import libcloud.compute.providers
from libcloud.compute.base import NodeDriver
import libcloud.dns.providers
from libcloud.dns.base import DNSDriver
import libcloud.loadbalancer.providers
from libcloud.loadbalancer.base import Driver
import libcloud.storage.providers
from libcloud.storage.base import StorageDriver
modules = [
(libcloud.compute.providers, NodeDriver),
(libcloud.dns.providers, DNSDriver),
(libcloud.loadbalancer.providers, Driver),
(libcloud.storage.providers, StorageDriver),
]
warnings = set()
def warning(obj, warning):
source_file = os.path.relpath(inspect.getsourcefile(obj), os.path.dirname(libcloud.__file__))
source_line = inspect.getsourcelines(obj)[1]
if (source_file, source_line, warning) in warnings:
# When the error is actually caused by a mixin or base class we can get dupes...
return
warnings.add((source_file, source_line, warning))
print source_file, source_line, warning
for providers, base, in modules:
core_api = {}
for name, value in inspect.getmembers(base, inspect.ismethod):
if name.startswith("_"):
continue
if name.startswith("ex_"):
warning(value, "Core driver shouldn't haveex_ methods")
continue
args = core_api[name] = inspect.getargspec(value)
for arg in args.args:
if arg.startswith("ex_"):
warning(value, "Core driver method shouldnt have ex_ arguments")
for driver_id in providers.DRIVERS.keys():
driver = providers.get_driver(driver_id)
for name, value in inspect.getmembers(driver, inspect.ismethod):
if name.startswith("_"):
continue
if not name.startswith("ex_") and not name in core_api:
warning(value, "'%s' should be prefixed with ex_ or be private as it is not a core API" % name)
continue
# Only validate arguments of core API's
if name.startswith("ex_"):
continue
argspec = inspect.getargspec(value)
core_args = set(core_api[name].args)
driver_args = set(argspec.args)
for missing in core_args - driver_args:
warning(value, "Core API function should support arg '%s' but doesn't" % missing)
for extra in driver_args - core_args:
if not extra.startswith("ex_"):
warning(value, "Core API function shouldn't take arg '%s'. Should it be prefixed with ex_?" % extra)