From 1a0ce47f8fe857630e61e000b553a24203bf983f Mon Sep 17 00:00:00 2001 From: Clark Boylan Date: Thu, 7 Jul 2016 17:06:21 -0700 Subject: [PATCH] Use a common requirements file We can use newer setuptools and pbr to use environment markers in our requirements files which allows us to select deps if the environment matches. With this we restrict the email dep to python < 3.0 which is the only dep we have that doesn't work across python 2 and 3. Note that this also updates the pbr machinery to pull in this new feature support. Part of this update requires updating hacking to avoid conflicting pbr requirements. This in turn has created some churn in the format of the code but should largely be a noop. Change-Id: I0be5dd8a6b33a51329077b5a5f4c7f5576829956 --- requirements-py3.txt | 29 ----------- requirements.txt | 4 +- setup.py | 13 +++-- storyboard/api/auth/openid_client.py | 2 +- storyboard/api/v1/stories.py | 2 +- storyboard/common/event_resolvers.py | 2 +- storyboard/common/master_branch_helper.py | 2 +- storyboard/db/api/boards.py | 4 +- storyboard/db/models.py | 2 +- storyboard/plugin/email/smtp_client.py | 24 ++++----- storyboard/plugin/event_worker.py | 4 +- storyboard/tests/api/auth/test_oauth.py | 49 +++++++++---------- .../notifications/test_notification_hook.py | 3 +- test-requirements.txt | 2 +- tox.ini | 17 +++---- 15 files changed, 68 insertions(+), 91 deletions(-) delete mode 100644 requirements-py3.txt diff --git a/requirements-py3.txt b/requirements-py3.txt deleted file mode 100644 index 5c54fa88..00000000 --- a/requirements-py3.txt +++ /dev/null @@ -1,29 +0,0 @@ -pbr>=0.6,!=0.7,<1.0 -jsonschema>=2.0.0,<3.0.0 -argparse -alembic>=0.4.1 -Babel>=1.3 -iso8601>=0.1.9 -oauthlib>=0.6 -oslo.config>=1.11.0 -oslo.context>=0.2.0 -oslo.utils>=1.4.0 -pecan>=0.4.5 -oslo.db>=1.8.0 -oslo.log>=1.0.0 -pika>=0.9.14 -python-openid -PyYAML>=3.1.0 -requests>=1.1 -six>=1.7.0 -SQLAlchemy>=0.9.7,<=0.9.99 -WSME>=0.6 -sqlalchemy-migrate>=0.9.1,!=0.9.2 -SQLAlchemy-FullText-Search -eventlet>=0.13.0 -stevedore>=1.3.0 -tzlocal>=1.1.2 -Jinja2>=2.7.3 -PyMySQL>=0.6.2,!=0.6.4 -apscheduler>=3.0.1 -python_dateutil>=2.4.0 diff --git a/requirements.txt b/requirements.txt index c12b1686..a65174cb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -pbr>=0.6,!=0.7,<1.0 +pbr>=1.6 jsonschema>=2.0.0,<3.0.0 alembic>=0.4.1 Babel>=1.3 @@ -22,7 +22,7 @@ SQLAlchemy-FullText-Search>=0.2.3 eventlet>=0.13.0 stevedore>=1.3.0 tzlocal>=1.1.2 -email>=4.0.2 +email>=4.0.2;python_version<'3.0' Jinja2>=2.7.3 PyMySQL>=0.6.2,!=0.6.4 apscheduler>=3.0.1,<3.1.0 diff --git a/setup.py b/setup.py index 70c2b3f3..2f8bfd2e 100755 --- a/setup.py +++ b/setup.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # Copyright (c) 2013 Hewlett-Packard Development Company, L.P. # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -14,9 +13,17 @@ # See the License for the specific language governing permissions and # limitations under the License. -# THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT +# This file was pulled from the openstack global requirements repo. import setuptools +# In python < 2.7.4, a lazy loading of package `pbr` will break +# setuptools if some other modules registered functions in `atexit`. +# solution from: http://bugs.python.org/issue15881#msg170215 +try: + import multiprocessing # noqa +except ImportError: + pass + setuptools.setup( - setup_requires=['pbr'], + setup_requires=['pbr>=1.8'], pbr=True) diff --git a/storyboard/api/auth/openid_client.py b/storyboard/api/auth/openid_client.py index f5917364..61bcfa44 100644 --- a/storyboard/api/auth/openid_client.py +++ b/storyboard/api/auth/openid_client.py @@ -138,7 +138,7 @@ class OpenIdClient(object): for token in verify_data_tokens) if (verify_response.status_code / 100 != 2 - or verify_dict['is_valid'] != 'true'): + or verify_dict['is_valid'] != 'true'): raise AccessDenied(redirect_uri=redirect_uri, message=e_msg.OPEN_ID_TOKEN_INVALID) diff --git a/storyboard/api/v1/stories.py b/storyboard/api/v1/stories.py index 14b0e885..18aa59d0 100644 --- a/storyboard/api/v1/stories.py +++ b/storyboard/api/v1/stories.py @@ -200,7 +200,7 @@ class StoriesController(rest.RestController): if not stories_api.story_can_create_story(story.story_type_id): abort(400, _("Can't create story of this type.")) - if not "tags" in story_dict or not story_dict["tags"]: + if "tags" not in story_dict or not story_dict["tags"]: story_dict["tags"] = [] # We can't set due dates when creating stories at the moment. diff --git a/storyboard/common/event_resolvers.py b/storyboard/common/event_resolvers.py index 6c84b10d..344b369e 100644 --- a/storyboard/common/event_resolvers.py +++ b/storyboard/common/event_resolvers.py @@ -70,7 +70,7 @@ def task_details_changed(event): def task_deleted(event): - #NOTE: There is nothing to resolve, as the task title is already stored in + # NOTE: There is nothing to resolve, as the task title is already stored in # the info. There is no way to store an id because the task is hard deleted # at the moment we would query it. return event diff --git a/storyboard/common/master_branch_helper.py b/storyboard/common/master_branch_helper.py index b04dec6d..f6c87fd8 100644 --- a/storyboard/common/master_branch_helper.py +++ b/storyboard/common/master_branch_helper.py @@ -14,7 +14,7 @@ # limitations under the License. -class MasterBranchHelper: +class MasterBranchHelper(object): name = "master" project_id = None expired = False diff --git a/storyboard/db/api/boards.py b/storyboard/db/api/boards.py index ad8ed1e6..a4e36fcb 100644 --- a/storyboard/db/api/boards.py +++ b/storyboard/db/api/boards.py @@ -216,8 +216,8 @@ def get_card(board, item_type, item_id, archived=False): for lane in board.lanes: for card in lane.worklist.items: if (card.item_type == item_type and - card.item_id == item_id and - card.archived == archived): + card.item_id == item_id and + card.archived == archived): return card diff --git a/storyboard/db/models.py b/storyboard/db/models.py index 018a0e0f..3be05d58 100644 --- a/storyboard/db/models.py +++ b/storyboard/db/models.py @@ -63,7 +63,7 @@ def table_args(): MYSQL_MEDIUM_TEXT = UnicodeText().with_variant(MEDIUMTEXT(), 'mysql') -class CommonLength: +class CommonLength(object): top_large_length = 255 top_middle_length = 100 top_short_length = 50 diff --git a/storyboard/plugin/email/smtp_client.py b/storyboard/plugin/email/smtp_client.py index a75c0484..cc2fccb0 100644 --- a/storyboard/plugin/email/smtp_client.py +++ b/storyboard/plugin/email/smtp_client.py @@ -33,19 +33,19 @@ class get_smtp_client(object): # SSL or not SSL? if not email_config.smtp_ssl_certfile \ or not email_config.smtp_ssl_keyfile: - self.s = smtplib.SMTP(host=email_config.smtp_host, - port=email_config.smtp_port, - local_hostname= - email_config.smtp_local_hostname, - timeout=email_config.smtp_timeout) + self.s = smtplib.SMTP( + host=email_config.smtp_host, + port=email_config.smtp_port, + local_hostname=email_config.smtp_local_hostname, + timeout=email_config.smtp_timeout) else: - self.s = smtplib.SMTP_SSL(host=email_config.smtp_host, - port=email_config.smtp_port, - keyfile=email_config.smtp_ssl_keyfile, - certfile=email_config.smtp_ssl_certfile, - local_hostname= - email_config.smtp_local_hostname, - timeout=email_config.smtp_timeout) + self.s = smtplib.SMTP_SSL( + host=email_config.smtp_host, + port=email_config.smtp_port, + keyfile=email_config.smtp_ssl_keyfile, + certfile=email_config.smtp_ssl_certfile, + local_hostname=email_config.smtp_local_hostname, + timeout=email_config.smtp_timeout) # Do we need to log in? if email_config.smtp_user and email_config.smtp_password: diff --git a/storyboard/plugin/event_worker.py b/storyboard/plugin/event_worker.py index 3c4290b1..2b472634 100644 --- a/storyboard/plugin/event_worker.py +++ b/storyboard/plugin/event_worker.py @@ -70,7 +70,7 @@ def terminate(sig, frame): signal.default_int_handler() -class DaemonManager(): +class DaemonManager(object): """A Daemon manager to handle multiple subprocesses. """ def __init__(self, child_process_count, daemon_method): @@ -134,7 +134,7 @@ class DaemonManager(): self._procs.append(process) -class PerpetualTimer(): +class PerpetualTimer(object): """A timer wrapper class that repeats itself. """ diff --git a/storyboard/tests/api/auth/test_oauth.py b/storyboard/tests/api/auth/test_oauth.py index 581048f2..4a764f99 100644 --- a/storyboard/tests/api/auth/test_oauth.py +++ b/storyboard/tests/api/auth/test_oauth.py @@ -391,11 +391,11 @@ class TestOAuthAuthorizeReturn(BaseOAuthTest): with base.HybridSessionManager(): token = auth_api.authorization_code_get(parameters['code']) + redirect_uri = self.valid_params['sb_redirect_uri'] # Validate the redirect response self.assertValidRedirect(response=response, expected_status_code=302, - redirect_uri= - self.valid_params['sb_redirect_uri'], + redirect_uri=redirect_uri, state=token.state, code=token.code) @@ -413,11 +413,11 @@ class TestOAuthAuthorizeReturn(BaseOAuthTest): state=random_state, **self.valid_params) + redirect_uri = self.valid_params['sb_redirect_uri'] # Validate the redirect response self.assertValidRedirect(response=response, expected_status_code=302, - redirect_uri= - self.valid_params['sb_redirect_uri'], + redirect_uri=redirect_uri, error='access_denied', error_description=e_msg.OPEN_ID_TOKEN_INVALID) @@ -438,11 +438,11 @@ class TestOAuthAuthorizeReturn(BaseOAuthTest): state=random_state, **invalid_params) + redirect_uri = self.valid_params['sb_redirect_uri'] # Validate the redirect response self.assertValidRedirect(response=response, expected_status_code=302, - redirect_uri= - self.valid_params['sb_redirect_uri'], + redirect_uri=redirect_uri, error='invalid_request', error_description=e_msg.INVALID_NO_NAME) @@ -463,11 +463,11 @@ class TestOAuthAuthorizeReturn(BaseOAuthTest): state=random_state, **invalid_params) + redirect_uri = self.valid_params['sb_redirect_uri'] # Validate the redirect response self.assertValidRedirect(response=response, expected_status_code=302, - redirect_uri= - self.valid_params['sb_redirect_uri'], + redirect_uri=redirect_uri, error='invalid_request', error_description=e_msg.INVALID_NO_EMAIL) @@ -522,14 +522,14 @@ class TestOAuthAccessToken(BaseOAuthTest): 'code': 'test_valid_code' }) + content_type = 'application/x-www-form-urlencoded' # POST with content: application/x-www-form-urlencoded response = self.app.post('/v1/openid/token', params={ 'code': authorization_code.code, 'grant_type': 'authorization_code' }, - content_type= - 'application/x-www-form-urlencoded', + content_type=content_type, expect_errors=True) # Assert that this is a successful response @@ -602,13 +602,13 @@ class TestOAuthAccessToken(BaseOAuthTest): 'expires_in': 300 }) + content_type = 'application/x-www-form-urlencoded' response = self.app.post('/v1/openid/token', params={ 'code': authorization_code.code, 'grant_type': 'authorization_code' }, - content_type= - 'application/x-www-form-urlencoded', + content_type=content_type, expect_errors=True) # Assert that this is a valid call. @@ -649,14 +649,14 @@ class TestOAuthAccessToken(BaseOAuthTest): 'created_at': expired }) + content_type = 'application/x-www-form-urlencoded' # POST with content: application/x-www-form-urlencoded response = self.app.post('/v1/openid/token', params={ 'code': authorization_code.code, 'grant_type': 'authorization_code' }, - content_type= - 'application/x-www-form-urlencoded', + content_type=content_type, expect_errors=True) # Assert that this is a valid call. @@ -682,14 +682,14 @@ class TestOAuthAccessToken(BaseOAuthTest): 'expires_in': 300 }) + content_type = 'application/x-www-form-urlencoded' # POST with content: application/x-www-form-urlencoded response = self.app.post('/v1/openid/token', params={ 'code': authorization_code.code, 'grant_type': 'invalid_grant_type' }, - content_type= - 'application/x-www-form-urlencoded', + content_type=content_type, expect_errors=True) # Assert that this is a successful response @@ -704,14 +704,14 @@ class TestOAuthAccessToken(BaseOAuthTest): appropriate error response. """ + content_type = 'application/x-www-form-urlencoded' # POST with content: application/x-www-form-urlencoded response = self.app.post('/v1/openid/token', params={ 'code': 'invalid_access_token', 'grant_type': 'invalid_grant_type' }, - content_type= - 'application/x-www-form-urlencoded', + content_type=content_type, expect_errors=True) # Assert that this is a successful response @@ -734,14 +734,14 @@ class TestOAuthAccessToken(BaseOAuthTest): 'code': 'test_valid_code' }) + content_type = 'application/x-www-form-urlencoded' # Generate an auth and a refresh token. resp_1 = self.app.post('/v1/openid/token', params={ 'code': authorization_code.code, 'grant_type': 'authorization_code' }, - content_type= - 'application/x-www-form-urlencoded', + content_type=content_type, expect_errors=True) # Assert that this is a successful response @@ -762,15 +762,14 @@ class TestOAuthAccessToken(BaseOAuthTest): self.assertIsNotNone(refresh_token) + content_type = 'application/x-www-form-urlencoded' # Issue a refresh token request. - resp_2 = self.app.post('/v1/openid/token', params={ 'refresh_token': t1['refresh_token'], 'grant_type': 'refresh_token' }, - content_type= - 'application/x-www-form-urlencoded', + content_type=content_type, expect_errors=True) # Assert that the response is good. @@ -832,14 +831,14 @@ class TestOAuthAccessToken(BaseOAuthTest): into a valid access token. """ + content_type = 'application/x-www-form-urlencoded' # Generate an auth and a refresh token. resp_1 = self.app.post('/v1/openid/token', params={ 'refresh_token': 'invalid_refresh_token', 'grant_type': 'refresh_token' }, - content_type= - 'application/x-www-form-urlencoded', + content_type=content_type, expect_errors=True) # Assert that this is a correct response diff --git a/storyboard/tests/notifications/test_notification_hook.py b/storyboard/tests/notifications/test_notification_hook.py index 981c5a1c..a5c99c1c 100644 --- a/storyboard/tests/notifications/test_notification_hook.py +++ b/storyboard/tests/notifications/test_notification_hook.py @@ -12,7 +12,8 @@ # implied. See the License for the specific language governing permissions and # limitations under the License. -from mock import patch, Mock +from mock import Mock +from mock import patch from storyboard.api.v1.v1_controller import V1Controller from storyboard.api.v1.wmodels import Task as TaskWmodel import storyboard.common.hook_priorities as priority diff --git a/test-requirements.txt b/test-requirements.txt index 857c4185..5037f2a3 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -1,4 +1,4 @@ -hacking>=0.5.6,<0.8 +hacking>=0.11.0,<0.12 coverage>=3.6 discover diff --git a/tox.ini b/tox.ini index 5e116541..eed9f67b 100644 --- a/tox.ini +++ b/tox.ini @@ -25,21 +25,20 @@ commands = rm -rf doc/source/apidoc doc/source/api python setup.py build_sphinx -[testenv:py34] -deps = -r{toxinidir}/requirements-py3.txt - -r{toxinidir}/test-requirements.txt - -[testenv:py35] -deps = -r{toxinidir}/requirements-py3.txt - -r{toxinidir}/test-requirements.txt - [testenv:cover] commands = python setup.py testr --coverage --testr-args='{posargs}' [flake8] # E125 and E128 are ignored on purpose, they are invalid pep8 # H803 is ignored on purpose - gating on periods in commit messages -ignore = E125,E128,H803 +# The following rules should either be addressed or determined to be +# skippable long term. +# H405 is ignored to make switch to newer hacking easier +# H234 is ignored to make switch to newer hacking easier +# H233 is ignored to make switch to newer hacking easier +# E265 is ignored to make switch to newer hacking easier +# H236 is ignored to make switch to newer hacking easier +ignore = E125,E128,H803,H405,H234,H233,E265,H236 builtins = _ show-source = True exclude=.venv,.git,.tox,dist,doc,*openstack/common*,*lib/python*,*egg,build