diff --git a/.travis.yml b/.travis.yml index cda9f5757..d54a8f1b4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,6 +7,10 @@ services: matrix: include: + - python: 3.5 + env: TOXENV=style-checks + - python: 3.6 + env: TOXENV=style-checks - python: 3.5 env: TOXENV=py35 - python: 3.5 @@ -15,8 +19,6 @@ matrix: env: TOXENV=py36 - python: 3.6 env: TOXENV=py36-functional - - python: 3.6 - env: TOXENV=update-pep8 - python: 3.6 env: TOXENV=docs - python: 3.6 diff --git a/README.md b/README.md index 283ea44bd..930952797 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,21 @@ From [PyPi](https://pypi.python.org/pypi/kubernetes_asyncio/) directly: pip install kubernetes_asyncio ``` +## Development +Install the development packages: + +```bash +pip install -r requirements.txt +pip install -r test-requirements.txt +``` + +You can run the style checks and tests with + +```bash +flake8 && isort -c +nosetests +``` + ## Example To list all pods: diff --git a/doc/source/conf.py b/doc/source/conf.py index a5f0a1fc6..af63591fb 100755 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -30,7 +30,7 @@ # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = [ 'sphinx.ext.autodoc', - #'sphinx.ext.intersphinx', + # 'sphinx.ext.intersphinx', ] # autodoc generation is a bit aggressive and a nuisance when doing heavy @@ -79,4 +79,4 @@ ] # Example configuration for intersphinx: refer to the Python standard library. -#intersphinx_mapping = {'http://docs.python.org/': None} +# intersphinx_mapping = {'http://docs.python.org/': None} diff --git a/kubernetes_asyncio/config/incluster_config_test.py b/kubernetes_asyncio/config/incluster_config_test.py index 622b31b37..91b4cc86a 100644 --- a/kubernetes_asyncio/config/incluster_config_test.py +++ b/kubernetes_asyncio/config/incluster_config_test.py @@ -17,8 +17,10 @@ import unittest from .config_exception import ConfigException -from .incluster_config import (SERVICE_HOST_ENV_NAME, SERVICE_PORT_ENV_NAME, - InClusterConfigLoader, _join_host_port) +from .incluster_config import ( + SERVICE_HOST_ENV_NAME, SERVICE_PORT_ENV_NAME, InClusterConfigLoader, + _join_host_port, +) _TEST_TOKEN = "temp_token" _TEST_CERT = "temp_cert" diff --git a/kubernetes_asyncio/config/kube_config_test.py b/kubernetes_asyncio/config/kube_config_test.py index 11c8dccf1..c394c173d 100644 --- a/kubernetes_asyncio/config/kube_config_test.py +++ b/kubernetes_asyncio/config/kube_config_test.py @@ -18,16 +18,17 @@ import shutil import tempfile import unittest +from types import SimpleNamespace import yaml from six import PY3 from .config_exception import ConfigException -from .dateutil import parse_rfc3339 -from .kube_config import (ConfigNode, FileOrData, KubeConfigLoader, - _cleanup_temp_files, _create_temp_file_with_content, - list_kube_config_contexts, load_kube_config, - new_client_from_config) +from .kube_config import ( + ConfigNode, FileOrData, KubeConfigLoader, _cleanup_temp_files, + _create_temp_file_with_content, list_kube_config_contexts, + load_kube_config, new_client_from_config, +) BEARER_TOKEN_FORMAT = "Bearer %s" @@ -525,9 +526,10 @@ def test_load_gcp_token_no_refresh(self): def test_load_gcp_token_with_refresh(self): - def cred(): return None - cred.token = TEST_ANOTHER_DATA_BASE64 - cred.expiry = datetime.datetime.now() + cred = SimpleNamespace( + token=TEST_ANOTHER_DATA_BASE64, + expiry=datetime.datetime.now(), + ) loader = KubeConfigLoader( config_dict=self.TEST_KUBE_CONFIG, diff --git a/kubernetes_asyncio/e2e_test/test_batch.py b/kubernetes_asyncio/e2e_test/test_batch.py index ae12d4f38..8b96d33b7 100644 --- a/kubernetes_asyncio/e2e_test/test_batch.py +++ b/kubernetes_asyncio/e2e_test/test_batch.py @@ -13,6 +13,7 @@ # under the License. import uuid + import asynctest from kubernetes_asyncio.client import api_client @@ -26,7 +27,6 @@ class TestClientBatch(asynctest.TestCase): def setUpClass(cls): cls.config = base.get_e2e_configuration() - async def test_job_apis(self): client = api_client.ApiClient(configuration=self.config) api = batch_v1_api.BatchV1Api(client) diff --git a/kubernetes_asyncio/e2e_test/test_client.py b/kubernetes_asyncio/e2e_test/test_client.py index c02d0dc65..46786d58d 100644 --- a/kubernetes_asyncio/e2e_test/test_client.py +++ b/kubernetes_asyncio/e2e_test/test_client.py @@ -12,9 +12,9 @@ # License for the specific language governing permissions and limitations # under the License. -import json import time import uuid + import asynctest from kubernetes_asyncio.client import api_client @@ -61,14 +61,12 @@ async def test_pod_apis(self): } } - resp = await api.create_namespaced_pod(body=pod_manifest, - namespace='default') + resp = await api.create_namespaced_pod(body=pod_manifest, namespace='default') self.assertEqual(name, resp.metadata.name) self.assertTrue(resp.status.phase) while True: - resp = await api.read_namespaced_pod(name=name, - namespace='default') + resp = await api.read_namespaced_pod(name=name, namespace='default') self.assertEqual(name, resp.metadata.name) self.assertTrue(resp.status.phase) if resp.status.phase != 'Pending': @@ -78,18 +76,18 @@ async def test_pod_apis(self): exec_command = ['/bin/sh', '-c', 'for i in $(seq 1 3); do date; done'] - resp = await api_ws.connect_get_namespaced_pod_exec(name, 'default', - command=exec_command, - stderr=False, stdin=False, - stdout=True, tty=False) + resp = await api_ws.connect_get_namespaced_pod_exec( + name, 'default', command=exec_command, + stderr=False, stdin=False, stdout=True, tty=False + ) print('EXEC response : %s' % resp) self.assertEqual(3, len(resp.splitlines())) exec_command = 'uptime' - resp = await api_ws.connect_post_namespaced_pod_exec(name, 'default', - command=exec_command, - stderr=False, stdin=False, - stdout=True, tty=False) + resp = await api_ws.connect_post_namespaced_pod_exec( + name, 'default', command=exec_command, + stderr=False, stdin=False, stdout=True, tty=False + ) print('EXEC response : %s' % resp) self.assertEqual(1, len(resp.splitlines())) @@ -97,8 +95,7 @@ async def test_pod_apis(self): number_of_pods = len(resp.items) self.assertTrue(number_of_pods > 0) - resp = await api.delete_namespaced_pod(name=name, body={}, - namespace='default') + resp = await api.delete_namespaced_pod(name=name, body={}, namespace='default') async def test_service_apis(self): client = api_client.ApiClient(configuration=self.config) @@ -116,28 +113,29 @@ async def test_service_apis(self): 'targetPort': 80}], 'selector': {'name': name}}} - resp = await api.create_namespaced_service(body=service_manifest, - namespace='default') + resp = await api.create_namespaced_service(body=service_manifest, namespace='default') self.assertEqual(name, resp.metadata.name) self.assertTrue(resp.status) - resp = await api.read_namespaced_service(name=name, - namespace='default') + resp = await api.read_namespaced_service(name=name, namespace='default') self.assertEqual(name, resp.metadata.name) self.assertTrue(resp.status) - service_manifest['spec']['ports'] = [{'name': 'new', - 'port': 8080, - 'protocol': 'TCP', - 'targetPort': 8080}] - resp = await api.patch_namespaced_service(body=service_manifest, - name=name, - namespace='default') + service_manifest['spec']['ports'] = [ + {'name': 'new', + 'port': 8080, + 'protocol': 'TCP', + 'targetPort': 8080} + ] + resp = await api.patch_namespaced_service( + body=service_manifest, + name=name, + namespace='default' + ) self.assertEqual(2, len(resp.spec.ports)) self.assertTrue(resp.status) - resp = await api.delete_namespaced_service(name=name, body={}, - namespace='default') + resp = await api.delete_namespaced_service(name=name, body={}, namespace='default') async def test_replication_controller_apis(self): client = api_client.ApiClient(configuration=self.config) @@ -147,17 +145,29 @@ async def test_replication_controller_apis(self): rc_manifest = { 'apiVersion': 'v1', 'kind': 'ReplicationController', - 'metadata': {'labels': {'name': name}, - 'name': name}, - 'spec': {'replicas': 2, - 'selector': {'name': name}, - 'template': {'metadata': { - 'labels': {'name': name}}, - 'spec': {'containers': [{ - 'image': 'nginx', - 'name': 'nginx', - 'ports': [{'containerPort': 80, - 'protocol': 'TCP'}]}]}}}} + 'metadata': { + 'labels': {'name': name}, + 'name': name + }, + 'spec': { + 'replicas': 2, + 'selector': { + 'name': name + }, + 'template': { + 'metadata': {'labels': {'name': name}}, + 'spec': { + 'containers': [ + { + 'image': 'nginx', + 'name': 'nginx', + 'ports': [{'containerPort': 80, 'protocol': 'TCP'}] + } + ] + } + } + } + } resp = await api.create_namespaced_replication_controller( body=rc_manifest, namespace='default') diff --git a/kubernetes_asyncio/e2e_test/test_extensions.py b/kubernetes_asyncio/e2e_test/test_extensions.py index 768f59529..15b246847 100644 --- a/kubernetes_asyncio/e2e_test/test_extensions.py +++ b/kubernetes_asyncio/e2e_test/test_extensions.py @@ -12,8 +12,9 @@ # License for the specific language governing permissions and limitations # under the License. -import asynctest import uuid + +import asynctest import yaml from kubernetes_asyncio.client import api_client diff --git a/kubernetes_asyncio/stream/ws_client.py b/kubernetes_asyncio/stream/ws_client.py index 115d6ae9f..effbc8274 100644 --- a/kubernetes_asyncio/stream/ws_client.py +++ b/kubernetes_asyncio/stream/ws_client.py @@ -10,11 +10,11 @@ # License for the specific language governing permissions and limitations # under the License. +from six.moves.urllib.parse import urlencode, urlparse, urlunparse + from kubernetes_asyncio.client import ApiClient -from six.moves.urllib.parse import urlencode, quote_plus, urlparse, urlunparse from kubernetes_asyncio.client.rest import RESTResponse - STDIN_CHANNEL = 0 STDOUT_CHANNEL = 1 STDERR_CHANNEL = 2 diff --git a/kubernetes_asyncio/stream/ws_client_test.py b/kubernetes_asyncio/stream/ws_client_test.py index 30dac9398..0dd909874 100644 --- a/kubernetes_asyncio/stream/ws_client_test.py +++ b/kubernetes_asyncio/stream/ws_client_test.py @@ -12,11 +12,11 @@ # See the License for the specific language governing permissions and # limitations under the License. -from asynctest import CoroutineMock, Mock, TestCase, patch +from asynctest import CoroutineMock, TestCase, patch -from kubernetes_asyncio.stream.ws_client import get_websocket_url, WsResponse -from kubernetes_asyncio import client, config +from kubernetes_asyncio import client from kubernetes_asyncio.stream import WsApiClient +from kubernetes_asyncio.stream.ws_client import WsResponse, get_websocket_url class WSClientTest(TestCase): @@ -53,7 +53,7 @@ async def __anext__(self): self.iter += 1 if self.iter > 5: raise StopAsyncIteration - return WsResponse((chr(1)+'mock').encode('utf-8')) + return WsResponse((chr(1) + 'mock').encode('utf-8')) mock = CoroutineMock() mock.RESTClientObject.return_value.pool_manager = mock @@ -67,10 +67,17 @@ async def __anext__(self): stdout=True, tty=False) ret = await resp - self.assertEqual(ret, 'mock'*5) - mock.ws_connect.assert_called_once_with('wss://localhost/api/v1/namespaces/namespace/pods/pod/exec?command=mock-command&stderr=True&stdin=False&stdout=True&tty=False', - headers={'sec-websocket-protocol': 'v4.channel.k8s.io', 'Accept': '*/*', 'User-Agent': - 'Swagger-Codegen/6.0.0-snapshot/python', 'Content-Type': 'application/json'}) + self.assertEqual(ret, 'mock' * 5) + mock.ws_connect.assert_called_once_with( + 'wss://localhost/api/v1/namespaces/namespace/pods/pod/exec?' + 'command=mock-command&stderr=True&stdin=False&stdout=True&tty=False', + headers={ + 'sec-websocket-protocol': 'v4.channel.k8s.io', + 'Accept': '*/*', + 'User-Agent': 'Swagger-Codegen/6.0.0-snapshot/python', + 'Content-Type': 'application/json' + } + ) if __name__ == '__main__': diff --git a/kubernetes_asyncio/watch/watch_test.py b/kubernetes_asyncio/watch/watch_test.py index b0a53acbb..68550c45a 100644 --- a/kubernetes_asyncio/watch/watch_test.py +++ b/kubernetes_asyncio/watch/watch_test.py @@ -84,7 +84,7 @@ async def test_watch_k8s_empty_response(self): # Iteration must cease after all valid responses were received. watch = kubernetes_asyncio.watch.Watch() cnt = 0 - async for _ in watch.stream(fake_api.get_namespaces): + async for _ in watch.stream(fake_api.get_namespaces): # noqa cnt += 1 self.assertEqual(cnt, len(side_effects)) @@ -137,7 +137,7 @@ async def test_watch_with_exception(self): with self.assertRaises(KeyError): watch = kubernetes_asyncio.watch.Watch() - async for e in watch.stream(fake_api.get_namespaces, timeout_seconds=10): + async for e in watch.stream(fake_api.get_namespaces, timeout_seconds=10): # noqa pass diff --git a/scripts/update-pep8.sh b/scripts/style-checks.sh similarity index 56% rename from scripts/update-pep8.sh rename to scripts/style-checks.sh index a6c470ae7..846c66bf7 100755 --- a/scripts/update-pep8.sh +++ b/scripts/style-checks.sh @@ -28,16 +28,10 @@ if [[ -z ${ENV} ]]; then fi SCRIPT_ROOT=$(dirname "${BASH_SOURCE}") -CLIENT_ROOT="${SCRIPT_ROOT}/../kubernetes_asyncio" +REPO_ROOT="${SCRIPT_ROOT}/../" +CLIENT_ROOT="${REPO_ROOT}/kubernetes_asyncio" pushd "${SCRIPT_ROOT}" > /dev/null -SCRIPT_ROOT=`pwd` -popd > /dev/null - -pushd "${CLIENT_ROOT}" > /dev/null -CLIENT_ROOT=`pwd` -popd > /dev/null - if [[ -z ${ENV} ]]; then echo "--- Creating virtualenv" virtualenv "${SCRIPT_ROOT}/.py" @@ -46,39 +40,12 @@ if [[ -z ${ENV} ]]; then trap "deactivate" EXIT SIGINT echo "--- Updating tools" - pip install --upgrade pep8 - pip install --upgrade autopep8 - pip install --upgrade isort + pip install --upgrade flake8 isort fi -SAVEIFS=$IFS -trap "IFS=$SAVEIFS" EXIT SIGINT -IFS=, - -SOURCES="${SCRIPT_ROOT}/../setup.py,${CLIENT_ROOT}/config/*.py,${CLIENT_ROOT}/watch/*.py,${SCRIPT_ROOT}/*.py,${CLIENT_ROOT}/../examples/*.py" - -echo "--- applying autopep8" -for SOURCE in $SOURCES; do - autopep8 -i -a -a $SOURCE -done - -echo "--- applying isort" -for SOURCE in $SOURCES; do - isort -y $SOURCE -done - -echo "--- check pep8 (all need to be fixed manually)" -set +o errexit -for SOURCE in $SOURCES; do - pep8 $SOURCE -done - -if [[ ! -z ${ENV} ]]; then - if [[ $(git status --porcelain) != "" ]]; then - cd "${SCRIPT_ROOT}/.." - git --no-pager diff - exit 1 - fi -fi +# Style checks. +flake8 +isort -c +popd echo "---Done." diff --git a/setup.cfg b/setup.cfg index 40fddba6a..2f0f3aa69 100644 --- a/setup.cfg +++ b/setup.cfg @@ -4,4 +4,27 @@ build-dir = doc/build all_files = 1 [upload_sphinx] -upload-dir = doc/build/html \ No newline at end of file +upload-dir = doc/build/html + +[flake8] +exclude = + .git, + build/ + scripts/ + kubernetes_asyncio/test/ + kubernetes_asyncio/client/ + **/__init__.py +max-line-length = 119 +ignore = D100,D101,D102,D103,D104,D105 + +[isort] +combine_as_imports = true +include_trailing_comma = true +line_length = 79 +multi_line_output = 5 +skip = + .git, + build/ + scripts/ + kubernetes_asyncio/test/ + kubernetes_asyncio/client/ diff --git a/setup.py b/setup.py index 5b95bab96..c72a53794 100644 --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from setuptools import find_packages, setup +from setuptools import setup # Do not edit these constants. They will be updated automatically # by scripts/update-client.sh. diff --git a/test-requirements.txt b/test-requirements.txt index ba5e7ef6c..ae0493cae 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -1,14 +1,14 @@ +asynctest +codecov>=1.4.0 coverage>=4.0.3 +flake8 +isort +mock>=2.0.0 nose>=1.3.7 -pytest pluggy>=0.3.1 py>=1.4.31 +pytest +pytest-xdist randomize>=0.13 -mock>=2.0.0 -sphinx>=1.2.1,!=1.3b1,<1.4 # BSD recommonmark -codecov>=1.4.0 -pep8 -autopep8 -isort -asynctest +sphinx>=1.2.1,!=1.3b1,<1.4 # BSD diff --git a/tox.ini b/tox.ini index 2ea71b325..637f23a5d 100644 --- a/tox.ini +++ b/tox.ini @@ -15,9 +15,9 @@ commands = commands = python setup.py build_sphinx -[testenv:update-pep8] +[testenv:style-checks] commands = - {toxinidir}/scripts/update-pep8.sh + {toxinidir}/scripts/style-checks.sh [testenv:py35-functional] commands =