diff --git a/glance/api/v1/upload_utils.py b/glance/api/v1/upload_utils.py index 2d52575ce7..fb748c47fd 100644 --- a/glance/api/v1/upload_utils.py +++ b/glance/api/v1/upload_utils.py @@ -242,7 +242,7 @@ def upload_data_to_store(req, image_meta, image_data, store, notifier): request=req, content_type='text/plain') - except exception.ImageSizeLimitExceeded as e: + except exception.ImageSizeLimitExceeded: msg = (_("Denying attempt to upload image larger than %d bytes.") % CONF.image_size_cap) LOG.warn(msg) @@ -273,7 +273,7 @@ def upload_data_to_store(req, image_meta, image_data, store, notifier): LOG.exception(msg) safe_kill(req, image_id, 'saving') - except (ValueError, IOError) as e: + except (ValueError, IOError): msg = _("Client disconnected before sending all data to backend") LOG.warn(msg) safe_kill(req, image_id, 'saving') @@ -281,7 +281,7 @@ def upload_data_to_store(req, image_meta, image_data, store, notifier): content_type="text/plain", request=req) - except Exception as e: + except Exception: msg = _("Failed to upload image %s") % image_id LOG.exception(msg) safe_kill(req, image_id, 'saving') diff --git a/glance/api/v2/image_data.py b/glance/api/v2/image_data.py index b15e4eb58f..a8ef61c6ce 100644 --- a/glance/api/v2/image_data.py +++ b/glance/api/v2/image_data.py @@ -234,7 +234,7 @@ class ImageDataController(object): LOG.exception(msg) raise webob.exc.HTTPConflict(explanation=e.msg, request=req) - except exception.Forbidden as e: + except exception.Forbidden: msg = ("Not allowed to upload image data for image %s" % image_id) LOG.debug(msg) @@ -283,16 +283,16 @@ class ImageDataController(object): self._delete(image_repo, image) raise webob.exc.HTTPBadRequest(explanation=msg) - except webob.exc.HTTPGone as e: + except webob.exc.HTTPGone: with excutils.save_and_reraise_exception(): LOG.error(_LE("Failed to upload image data due to HTTP error")) - except webob.exc.HTTPError as e: + except webob.exc.HTTPError: with excutils.save_and_reraise_exception(): LOG.error(_LE("Failed to upload image data due to HTTP error")) self._restore(image_repo, image) - except Exception as e: + except Exception: with excutils.save_and_reraise_exception(): LOG.error(_LE("Failed to upload image data due to " "internal error")) @@ -346,7 +346,7 @@ class ImageDataController(object): staging_store.add( image_id, utils.LimitingReader( utils.CooperativeReader(data), CONF.image_size_cap), 0) - except glance_store.Duplicate as e: + except glance_store.Duplicate: msg = _("The image %s has data on staging") % image_id raise webob.exc.HTTPConflict(explanation=msg) @@ -390,7 +390,7 @@ class ImageDataController(object): LOG.debug(msg) raise webob.exc.HTTPConflict(explanation=e.msg, request=req) - except Exception as e: + except Exception: with excutils.save_and_reraise_exception(): LOG.exception(_LE("Failed to stage image data due to " "internal error")) diff --git a/glance/api/v2/images.py b/glance/api/v2/images.py index 7edb10752a..c479999077 100644 --- a/glance/api/v2/images.py +++ b/glance/api/v2/images.py @@ -182,12 +182,10 @@ class ImagesController(object): 'backend': stores} if (import_method == 'web-download' and - not utils.validate_import_uri(uri)): - LOG.debug("URI for web-download does not pass filtering: %s", - uri) - msg = (_("URI for web-download does not pass filtering: %s") % - uri) - raise webob.exc.HTTPBadRequest(explanation=msg) + not utils.validate_import_uri(uri)): + LOG.debug("URI for web-download does not pass filtering: %s", uri) + msg = (_("URI for web-download does not pass filtering: %s") % uri) + raise webob.exc.HTTPBadRequest(explanation=msg) try: import_task = task_factory.new_task(task_type='api_image_import', @@ -506,7 +504,7 @@ class ImagesController(object): except (glance_store.Forbidden, exception.Forbidden) as e: LOG.debug("User not permitted to delete image '%s'", image_id) raise webob.exc.HTTPForbidden(explanation=e.msg) - except (glance_store.NotFound, exception.NotFound) as e: + except (glance_store.NotFound, exception.NotFound): msg = (_("Failed to find image %(image_id)s to delete") % {'image_id': image_id}) LOG.warn(msg) @@ -1176,8 +1174,8 @@ class ResponseSerializer(wsgi.JSONResponseSerializer): locations = _get_image_locations(image) if locations: # Choose best location configured strategy - l = location_strategy.choose_best_location(locations) - image_view['direct_url'] = l['url'] + loc = location_strategy.choose_best_location(locations) + image_view['direct_url'] = loc['url'] else: LOG.debug("The 'locations' list of image %s is empty, " "not including 'direct_url' in response", diff --git a/glance/api/v2/metadef_resource_types.py b/glance/api/v2/metadef_resource_types.py index ef5956fcf5..b64dba11bc 100644 --- a/glance/api/v2/metadef_resource_types.py +++ b/glance/api/v2/metadef_resource_types.py @@ -128,7 +128,7 @@ class ResourceTypeController(object): LOG.debug("User not permitted to delete metadata resource type " "'%s' within '%s' namespace", resource_type, namespace) raise webob.exc.HTTPForbidden(explanation=e.msg) - except exception.NotFound as e: + except exception.NotFound: msg = (_("Failed to find resource type %(resourcetype)s to " "delete") % {'resourcetype': resource_type}) LOG.error(msg) diff --git a/glance/cmd/cache_manage.py b/glance/cmd/cache_manage.py index 26021a4815..23f7e982d6 100644 --- a/glance/cmd/cache_manage.py +++ b/glance/cmd/cache_manage.py @@ -524,5 +524,6 @@ def main(): except (RuntimeError, NotImplementedError) as e: sys.exit("ERROR: %s" % e) + if __name__ == '__main__': main() diff --git a/glance/cmd/replicator.py b/glance/cmd/replicator.py index 5394c975ff..f2d2ae6b1f 100644 --- a/glance/cmd/replicator.py +++ b/glance/cmd/replicator.py @@ -757,7 +757,7 @@ def main(): config.parse_args() except RuntimeError as e: sys.exit("ERROR: %s" % encodeutils.exception_to_unicode(e)) - except SystemExit as e: + except SystemExit: sys.exit("Please specify one command") # Setup logging diff --git a/glance/common/swift_store_utils.py b/glance/common/swift_store_utils.py index 945c8e6636..ba971bdbd7 100644 --- a/glance/common/swift_store_utils.py +++ b/glance/common/swift_store_utils.py @@ -136,7 +136,7 @@ class SwiftParams(object): reference['user'] = CONFIG.get(ref, 'user') reference['key'] = CONFIG.get(ref, 'key') account_params[ref] = reference - except (ValueError, SyntaxError, configparser.NoOptionError) as e: + except (ValueError, SyntaxError, configparser.NoOptionError): LOG.exception(_LE("Invalid format of swift store config " "cfg")) return account_params diff --git a/glance/common/timeutils.py b/glance/common/timeutils.py index 93ab868ed4..756896791b 100644 --- a/glance/common/timeutils.py +++ b/glance/common/timeutils.py @@ -76,6 +76,7 @@ def iso8601_from_timestamp(timestamp, microsecond=False): """Returns an iso8601 formatted date from timestamp.""" return isotime(datetime.datetime.utcfromtimestamp(timestamp), microsecond) + utcnow.override_time = None diff --git a/glance/common/wsgi.py b/glance/common/wsgi.py index 0066f7b04e..8f947be6ad 100644 --- a/glance/common/wsgi.py +++ b/glance/common/wsgi.py @@ -970,7 +970,7 @@ class APIMapper(routes.Mapper): """ def routematch(self, url=None, environ=None): - if url is "": + if url == "": result = self._match("", environ) return result[0], result[1] return routes.Mapper.routematch(self, url, environ) diff --git a/glance/common/wsgi_app.py b/glance/common/wsgi_app.py index 51a92e2737..5dbdf6abd4 100644 --- a/glance/common/wsgi_app.py +++ b/glance/common/wsgi_app.py @@ -19,6 +19,7 @@ import osprofiler.initializer from glance.common import config from glance.common import store_utils +from glance.i18n import _ from glance import notifier CONF = cfg.CONF diff --git a/glance/db/simple/api.py b/glance/db/simple/api.py index 40d11213be..94e07c5a22 100644 --- a/glance/db/simple/api.py +++ b/glance/db/simple/api.py @@ -1056,8 +1056,11 @@ def _sort_tasks(tasks, sort_key, sort_dir): reverse = False if tasks and not (sort_key in tasks[0]): raise exception.InvalidSortKey() - keyfn = lambda x: (x[sort_key] if x[sort_key] is not None else '', - x['created_at'], x['id']) + + def keyfn(x): + return (x[sort_key] if x[sort_key] is not None else '', + x['created_at'], x['id']) + reverse = sort_dir == 'desc' tasks.sort(key=keyfn, reverse=reverse) return tasks diff --git a/glance/db/sqlalchemy/alembic_migrations/env.py b/glance/db/sqlalchemy/alembic_migrations/env.py index ac24eca88f..554aa5a943 100644 --- a/glance/db/sqlalchemy/alembic_migrations/env.py +++ b/glance/db/sqlalchemy/alembic_migrations/env.py @@ -82,6 +82,7 @@ def run_migrations_online(): with context.begin_transaction(): context.run_migrations() + if context.is_offline_mode(): run_migrations_offline() else: diff --git a/glance/db/sqlalchemy/metadef_api/namespace.py b/glance/db/sqlalchemy/metadef_api/namespace.py index d0aac991e3..8933751148 100644 --- a/glance/db/sqlalchemy/metadef_api/namespace.py +++ b/glance/db/sqlalchemy/metadef_api/namespace.py @@ -182,7 +182,7 @@ def _get_all_by_resource_types(context, session, filters, marker=None, for name, namespace_id in db_recs: namespace_id_list.append(namespace_id) - if len(namespace_id_list) is 0: + if len(namespace_id_list) == 0: return [] filters2 = filters diff --git a/glance/db/sqlalchemy/migrate_repo/schema.py b/glance/db/sqlalchemy/migrate_repo/schema.py index 9d40ea64ce..9e17e566c0 100644 --- a/glance/db/sqlalchemy/migrate_repo/schema.py +++ b/glance/db/sqlalchemy/migrate_repo/schema.py @@ -26,32 +26,40 @@ from glance.i18n import _LI LOG = logging.getLogger(__name__) -String = lambda length: sqlalchemy.types.String( - length=length, convert_unicode=False, - unicode_error=None, _warn_on_bytestring=False) +def String(length): + return sqlalchemy.types.String( + length=length, convert_unicode=False, + unicode_error=None, _warn_on_bytestring=False) -Text = lambda: sqlalchemy.types.Text( - length=None, convert_unicode=False, - unicode_error=None, _warn_on_bytestring=False) +def Text(): + return sqlalchemy.types.Text( + length=None, convert_unicode=False, + unicode_error=None, _warn_on_bytestring=False) -Boolean = lambda: sqlalchemy.types.Boolean(create_constraint=True, name=None) +def Boolean(): + return sqlalchemy.types.Boolean(create_constraint=True, name=None) -DateTime = lambda: sqlalchemy.types.DateTime(timezone=False) +def DateTime(): + return sqlalchemy.types.DateTime(timezone=False) -Integer = lambda: sqlalchemy.types.Integer() +def Integer(): + return sqlalchemy.types.Integer() -BigInteger = lambda: sqlalchemy.types.BigInteger() +def BigInteger(): + return sqlalchemy.types.BigInteger() -PickleType = lambda: sqlalchemy.types.PickleType() +def PickleType(): + return sqlalchemy.types.PickleType() -Numeric = lambda: sqlalchemy.types.Numeric() +def Numeric(): + return sqlalchemy.types.Numeric() def from_migration_import(module_name, fromlist): diff --git a/glance/db/sqlalchemy/migrate_repo/versions/003_add_disk_format.py b/glance/db/sqlalchemy/migrate_repo/versions/003_add_disk_format.py index c67b32a7fc..95c249989b 100644 --- a/glance/db/sqlalchemy/migrate_repo/versions/003_add_disk_format.py +++ b/glance/db/sqlalchemy/migrate_repo/versions/003_add_disk_format.py @@ -13,7 +13,7 @@ # License for the specific language governing permissions and limitations # under the License. -from sqlalchemy import * # noqa +from sqlalchemy import Column, MetaData, Table, and_, select from glance.db.sqlalchemy.migrate_repo.schema import ( Boolean, DateTime, Integer, String, Text, from_migration_import) # noqa diff --git a/glance/db/sqlalchemy/migrate_repo/versions/004_add_checksum.py b/glance/db/sqlalchemy/migrate_repo/versions/004_add_checksum.py index ea70fe217c..d44cdee1ae 100644 --- a/glance/db/sqlalchemy/migrate_repo/versions/004_add_checksum.py +++ b/glance/db/sqlalchemy/migrate_repo/versions/004_add_checksum.py @@ -13,7 +13,7 @@ # License for the specific language governing permissions and limitations # under the License. -from sqlalchemy import * # noqa +from sqlalchemy import Column, MetaData, Table from glance.db.sqlalchemy.migrate_repo.schema import ( Boolean, DateTime, Integer, String, Text, from_migration_import) # noqa diff --git a/glance/db/sqlalchemy/migrate_repo/versions/005_size_big_integer.py b/glance/db/sqlalchemy/migrate_repo/versions/005_size_big_integer.py index b3d8616994..1c9fa2af5b 100644 --- a/glance/db/sqlalchemy/migrate_repo/versions/005_size_big_integer.py +++ b/glance/db/sqlalchemy/migrate_repo/versions/005_size_big_integer.py @@ -13,7 +13,7 @@ # License for the specific language governing permissions and limitations # under the License. -from sqlalchemy import * # noqa +from sqlalchemy import Column, MetaData, Table from glance.db.sqlalchemy.migrate_repo.schema import ( Boolean, DateTime, BigInteger, Integer, String, diff --git a/glance/db/sqlalchemy/migrate_repo/versions/006_key_to_name.py b/glance/db/sqlalchemy/migrate_repo/versions/006_key_to_name.py index 2ec99e9631..c613e9c1f1 100644 --- a/glance/db/sqlalchemy/migrate_repo/versions/006_key_to_name.py +++ b/glance/db/sqlalchemy/migrate_repo/versions/006_key_to_name.py @@ -13,7 +13,7 @@ # License for the specific language governing permissions and limitations # under the License. -from sqlalchemy import * # noqa +from sqlalchemy import Index, MetaData from glance.db.sqlalchemy.migrate_repo.schema import from_migration_import diff --git a/glance/db/sqlalchemy/migrate_repo/versions/007_add_owner.py b/glance/db/sqlalchemy/migrate_repo/versions/007_add_owner.py index e43136be57..76fec3c4d8 100644 --- a/glance/db/sqlalchemy/migrate_repo/versions/007_add_owner.py +++ b/glance/db/sqlalchemy/migrate_repo/versions/007_add_owner.py @@ -13,7 +13,7 @@ # License for the specific language governing permissions and limitations # under the License. -from sqlalchemy import * # noqa +from sqlalchemy import Column, MetaData, Table from glance.db.sqlalchemy.migrate_repo.schema import ( Boolean, DateTime, BigInteger, Integer, String, diff --git a/glance/db/sqlalchemy/migrate_repo/versions/008_add_image_members_table.py b/glance/db/sqlalchemy/migrate_repo/versions/008_add_image_members_table.py index 2172e105bb..fe13c11699 100644 --- a/glance/db/sqlalchemy/migrate_repo/versions/008_add_image_members_table.py +++ b/glance/db/sqlalchemy/migrate_repo/versions/008_add_image_members_table.py @@ -13,7 +13,8 @@ # License for the specific language governing permissions and limitations # under the License. -from sqlalchemy import * # noqa +from sqlalchemy import Column, ForeignKey, Index, MetaData, Table +from sqlalchemy import UniqueConstraint from glance.db.sqlalchemy.migrate_repo.schema import ( Boolean, DateTime, Integer, String, create_tables, diff --git a/glance/db/sqlalchemy/migrate_repo/versions/009_add_mindisk_and_minram.py b/glance/db/sqlalchemy/migrate_repo/versions/009_add_mindisk_and_minram.py index 0c45ab76f5..5c919a9961 100644 --- a/glance/db/sqlalchemy/migrate_repo/versions/009_add_mindisk_and_minram.py +++ b/glance/db/sqlalchemy/migrate_repo/versions/009_add_mindisk_and_minram.py @@ -13,7 +13,7 @@ # License for the specific language governing permissions and limitations # under the License. -from sqlalchemy import * # noqa +from sqlalchemy import Column, MetaData, Table from glance.db.sqlalchemy.migrate_repo.schema import ( Boolean, DateTime, Integer, String, Text) # noqa diff --git a/glance/db/sqlalchemy/migrate_repo/versions/010_default_update_at.py b/glance/db/sqlalchemy/migrate_repo/versions/010_default_update_at.py index 9d1b15162e..c413e28054 100644 --- a/glance/db/sqlalchemy/migrate_repo/versions/010_default_update_at.py +++ b/glance/db/sqlalchemy/migrate_repo/versions/010_default_update_at.py @@ -13,7 +13,7 @@ # License for the specific language governing permissions and limitations # under the License. -from sqlalchemy import * # noqa +from sqlalchemy import MetaData from glance.db.sqlalchemy.migrate_repo.schema import from_migration_import diff --git a/glance/i18n.py b/glance/i18n.py index 0a8ac675c7..fbbc28d2f0 100644 --- a/glance/i18n.py +++ b/glance/i18n.py @@ -13,14 +13,28 @@ # License for the specific language governing permissions and limitations # under the License. -from oslo_i18n import * # noqa +import oslo_i18n as i18n -_translators = TranslatorFactory(domain='glance') +DOMAIN = 'glance' + +_translators = i18n.TranslatorFactory(domain=DOMAIN) # The primary translation function using the well-known name "_" _ = _translators.primary +def enable_lazy(enable=True): + return i18n.enable_lazy(enable) + + +def translate(value, user_locale=None): + return i18n.translate(value, user_locale) + + +def get_available_languages(domain=DOMAIN): + return i18n.get_available_languages(domain) + + # i18n log translation functions are deprecated. While removing the invocations # requires a lot of reviewing effort, we decide to make it as no-op functions. def _LI(msg): diff --git a/glance/tests/functional/db/base.py b/glance/tests/functional/db/base.py index 67446ca76b..c416c8e3a5 100644 --- a/glance/tests/functional/db/base.py +++ b/glance/tests/functional/db/base.py @@ -1383,7 +1383,9 @@ class DriverTests(object): return def _assertMemberListMatch(list1, list2): - _simple = lambda x: set([(o['member'], o['image_id']) for o in x]) + def _simple(x): + return set([(o['member'], o['image_id']) for o in x]) + self.assertEqual(_simple(list1), _simple(list2)) # NOTE(flaper87): Update auth token, otherwise diff --git a/glance/tests/functional/v2/test_metadef_resourcetypes.py b/glance/tests/functional/v2/test_metadef_resourcetypes.py index cc047d6111..36a60b32fc 100644 --- a/glance/tests/functional/v2/test_metadef_resourcetypes.py +++ b/glance/tests/functional/v2/test_metadef_resourcetypes.py @@ -122,7 +122,7 @@ class ResourceTypeController(object): raise exception.NotFound() except exception.Forbidden as e: raise webob.exc.HTTPForbidden(explanation=e.msg) - except exception.NotFound as e: + except exception.NotFound: msg = (_("Failed to find resource type %(resourcetype)s to " "delete") % {'resourcetype': resource_type}) LOG.error(msg) diff --git a/glance/tests/unit/common/test_wsgi.py b/glance/tests/unit/common/test_wsgi.py index 94dd905f91..f9ce557e78 100644 --- a/glance/tests/unit/common/test_wsgi.py +++ b/glance/tests/unit/common/test_wsgi.py @@ -394,9 +394,9 @@ class ResourceTest(test_utils.BaseTestCase): def test_response_headers_encoded(self): # prepare environment - for_openstack_comrades = \ - u'\u0417\u0430 \u043e\u043f\u0435\u043d\u0441\u0442\u0435\u043a, ' \ - u'\u0442\u043e\u0432\u0430\u0440\u0438\u0449\u0438' + for_openstack_comrades = ( + u'\u0417\u0430 \u043e\u043f\u0435\u043d\u0441\u0442\u0435\u043a, ' + u'\u0442\u043e\u0432\u0430\u0440\u0438\u0449\u0438') class FakeController(object): def index(self, shirt, pants=None): diff --git a/glance/tests/unit/test_db.py b/glance/tests/unit/test_db.py index 07ca2dcac0..e1ac678963 100644 --- a/glance/tests/unit/test_db.py +++ b/glance/tests/unit/test_db.py @@ -61,6 +61,7 @@ class TestDbUtilities(test_utils.BaseTestCase): import_module.assert_called_once_with('glance.db.sqlalchemy.api') api.configure.assert_called_once_with() + UUID1 = 'c80a1a6c-bd1f-41c5-90ee-81afedb1d58d' UUID2 = 'a85abd86-55b3-4d5b-b0b4-5d0a6e6042fc' UUID3 = '971ec09a-8067-4bc8-a91f-ae3557f1c4c7' diff --git a/glance/tests/unit/test_scrubber.py b/glance/tests/unit/test_scrubber.py index cea4dbbe38..b7a1b93e55 100644 --- a/glance/tests/unit/test_scrubber.py +++ b/glance/tests/unit/test_scrubber.py @@ -178,12 +178,11 @@ class ImagePager(object): page_size = image_count self.image_batches = [] start = 0 - l = len(images) - while start < l: + while start < image_count: self.image_batches.append(images[start: start + page_size]) start += page_size - if (l - start) < page_size: - page_size = l - start + if (image_count - start) < page_size: + page_size = image_count - start def __call__(self): if len(self.image_batches) == 0: diff --git a/glance/tests/unit/utils.py b/glance/tests/unit/utils.py index 92cf31567b..5765c08104 100644 --- a/glance/tests/unit/utils.py +++ b/glance/tests/unit/utils.py @@ -65,7 +65,7 @@ def sort_url_by_qs_keys(url): def get_fake_request(path='', method='POST', is_admin=False, user=USER1, roles=None, tenant=TENANT1): if roles is None: - roles = ['member'] + roles = ['member'] req = wsgi.Request.blank(path) req.method = method diff --git a/glance/tests/unit/v2/test_tasks_resource.py b/glance/tests/unit/v2/test_tasks_resource.py index 57653414d6..79d31d0435 100644 --- a/glance/tests/unit/v2/test_tasks_resource.py +++ b/glance/tests/unit/v2/test_tasks_resource.py @@ -84,6 +84,7 @@ def _domain_fixture(task_id, **kwargs): task = glance.domain.Task(**task_properties) return task + CONF = cfg.CONF CONF.import_opt('task_time_to_live', 'glance.common.config', group='task') diff --git a/lower-constraints.txt b/lower-constraints.txt index d55616b043..af3b93ffcc 100644 --- a/lower-constraints.txt +++ b/lower-constraints.txt @@ -36,7 +36,7 @@ gitdb2==2.0.3 GitPython==2.1.8 glance-store==1.0.0 greenlet==0.4.13 -hacking==0.12.0 +hacking==2.0.0 httplib2==0.9.1 idna==2.6 imagesize==1.0.0 diff --git a/test-requirements.txt b/test-requirements.txt index d4478ece00..a51071d998 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -3,7 +3,7 @@ # process, which may cause wedges in the gate later. # Hacking already pins down pep8, pyflakes and flake8 -hacking!=0.13.0,<0.14,>=0.12.0 # Apache-2.0 +hacking>=2.0.0 # Apache-2.0 # For translations processing Babel!=2.4.0,>=2.3.4 # BSD diff --git a/tox.ini b/tox.ini index 0ac852fecd..d450277834 100644 --- a/tox.ini +++ b/tox.ini @@ -118,11 +118,14 @@ ignore-path = .venv,.git,.tox,*glance/locale*,*lib/python*,glance.egg*,api-ref/b [flake8] # TODO(dmllr): Analyze or fix the warnings blacklisted below +# E402 module level import not at top of file # E711 comparison to None should be 'if cond is not None:' # E712 comparison to True should be 'if cond is True:' or 'if cond:' # H404 multi line docstring should start with a summary # H405 multi line docstring summary not separated with an empty line -ignore = E711,E712,H404,H405 +# W503 line break before binary operator - conflicting guidance +# W504 line break after binary operator - conflicting guidance +ignore = E402,E711,E712,H404,H405,W503,W504 exclude = .venv,.git,.tox,dist,doc,etc,*glance/locale*,*lib/python*,*egg,build [hacking]