diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 000000000000..08ccf02e2941 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,142 @@ +--- +version: 2 +jobs: + build: + docker: + - image: googleapis/nox:0.11.2 + steps: + - checkout + - run: + name: Decrypt credentials + command: | + if [ -n "$GOOGLE_APPLICATION_CREDENTIALS" ]; then + apt-get update && apt-get install -y openssl + openssl aes-256-cbc -d -a -k "$GOOGLE_CREDENTIALS_PASSPHRASE" \ + -in /var/code/gcp/test_utils/credentials.json.enc \ + -out "$GOOGLE_APPLICATION_CREDENTIALS" + else + echo "No credentials. System tests will not run." + fi + - run: + name: Add GitHub public key to known hosts + command: | + mkdir -p ~/.ssh/ + if [[ ! -f ~/.ssh/known_hosts ]] || ! grep "github.com" ~/.ssh/known_hosts; then + echo " + github.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ== + " >> ~/.ssh/known_hosts + fi + - run: + name: Declare target packages (changed packages and dependencies). + command: | + python3.6 test_utils/scripts/get_target_packages.py > ~/target_packages + cat ~/target_packages + - run: + name: Run tests - google.cloud.core + command: | + if [[ -n $(grep core ~/target_packages) ]]; then + nox -f core/nox.py + fi + - run: + name: Run tests - google.cloud.bigquery + command: | + if [[ -n $(grep bigquery ~/target_packages) ]]; then + nox -f bigquery/nox.py + fi + - run: + name: Run tests - google.cloud.bigtable + command: | + if [[ -n $(grep bigtable ~/target_packages) ]]; then + nox -f bigtable/nox.py + fi + - run: + name: Run tests - google.cloud.datastore + command: | + if [[ -n $(grep datastore ~/target_packages) ]]; then + nox -f datastore/nox.py + fi + - run: + name: Run tests - google.cloud.dns + command: | + if [[ -n $(grep dns ~/target_packages) ]]; then + nox -f dns/nox.py + fi + - run: + name: Run tests - google.cloud.error_reporting + command: | + if [[ -n $(grep error_reporting ~/target_packages) ]]; then + nox -f error_reporting/nox.py + fi + - run: + name: Run tests - google.cloud.language + command: | + if [[ -n $(grep language ~/target_packages) ]]; then + nox -f language/nox.py + fi + - run: + name: Run tests - google.cloud.logging + command: | + if [[ -n $(grep logging ~/target_packages) ]]; then + nox -f logging/nox.py + fi + - run: + name: Run tests - google.cloud.monitoring + command: | + if [[ -n $(grep monitoring ~/target_packages) ]]; then + nox -f monitoring/nox.py + fi + - run: + name: Run tests - google.cloud.pubsub + command: | + if [[ -n $(grep pubsub ~/target_packages) ]]; then + nox -f pubsub/nox.py + fi + - run: + name: Run tests - google.cloud.resource_manager + command: | + if [[ -n $(grep resource_manager ~/target_packages) ]]; then + nox -f resource_manager/nox.py + fi + - run: + name: Run tests - google.cloud.runtimeconfig + command: | + if [[ -n $(grep runtimeconfig ~/target_packages) ]]; then + nox -f runtimeconfig/nox.py + fi + - run: + name: Run tests - google.cloud.spanner + command: | + if [[ -n $(grep spanner ~/target_packages) ]]; then + nox -f spanner/nox.py + fi + - run: + name: Run tests - google.cloud.speech + command: | + if [[ -n $(grep speech ~/target_packages) ]]; then + nox -f speech/nox.py + fi + - run: + name: Run tests - google.cloud.storage + command: | + if [[ -n $(grep storage ~/target_packages) ]]; then + nox -f storage/nox.py + fi + - run: + name: Run tests - google.cloud.translate + command: | + if [[ -n $(grep translate ~/target_packages) ]]; then + nox -f translate/nox.py + fi + - run: + name: Run tests - google.cloud.vision + command: | + if [[ -n $(grep vision ~/target_packages) ]]; then + nox -f vision/nox.py + fi + - deploy: + name: Update the docs + command: nox -e docs + - deploy: + name: Push to PyPI (if this is a release tag). + command: test_utils/scripts/circleci/twine_upload.sh + working_directory: /var/code/gcp/ diff --git a/.gitignore b/.gitignore index 8e4001cb4b12..cf78b1e3e91a 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,7 @@ pip-log.txt # Unit test / coverage reports .coverage +.nox .tox .cache diff --git a/.travis.yml b/.travis.yml index 8251e037e87c..bbc945a24410 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,29 +1,6 @@ -language: python -sudo: false - -addons: - apt: - sources: - - deadsnakes - packages: - - python3.5 - -install: - - pip install --upgrade pip tox - -script: - - python2.7 scripts/run_unit_tests.py - - if [[ "${TRAVIS_EVENT_TYPE}" != "pull_request" ]]; then python3.4 scripts/run_unit_tests.py; fi - - python3.5 scripts/run_unit_tests.py - - python scripts/run_unit_tests.py --tox-env cover - - tox -e lint - - tox -e system-tests - - tox -e system-tests3 - - scripts/update_docs.sh - -after_success: - - scripts/coveralls.sh - -cache: - directories: - - ${HOME}/.cache/pip +--- +# Exclude Travis completely. +# We can remove this file post-merge. +branches: + exclude: + - /.*/ diff --git a/appveyor.yml b/appveyor.yml index 1df247d6cb34..9fb9bb6dd3e8 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -18,20 +18,22 @@ environment: # iterations. # Python 2.7.11 is the latest Python 2.7 with a Windows installer - # Python 2.7.11 is the overall latest + # Python 2.7.13 is the overall latest # https://www.python.org/ftp/python/2.7.11/ - PYTHON: "C:\\Python27" PYTHON_VERSION: "2.7.11" + PYTHON_MAJMIN_VERSION: "2.7" PYTHON_ARCH: "32" - TOX_ENV: "py27" + NOX_ENV: "unit_tests(python_version='2.7')" # Python 3.5.1 is the latest Python 3.5 with a Windows installer - # Python 3.5.1 is the overall latest + # Python 3.5.3 is the overall latest # https://www.python.org/ftp/python/3.5.1/ - PYTHON: "C:\\Python35-x64" PYTHON_VERSION: "3.5.1" + PYTHON_MAJMIN_VERSION: "3.5" PYTHON_ARCH: "64" - TOX_ENV: "py35" + NOX_ENV: "unit_tests(python_version='3.5')" install: - ECHO "Filesystem root:" @@ -69,8 +71,37 @@ build_script: test_script: - "%CMD_IN_ENV% pip list" + + # AppVeyor gets confused by a nox.py file in the root, and tries to + # import from it instead of importing actual nox. + # + # This is an annoying problem, but we do not need to build docs here, + # so this is a hack-solution to get around it. I am okay with this in the + # short term because docs will move into their own packages soon, and this + # file will disappear completely. + - '%CMD_IN_ENV% del nox.py' + + - '%CMD_IN_ENV% dir' + - '%CMD_IN_ENV% dir bigquery' + # Run the project tests - - "%CMD_IN_ENV% tox -e %TOX_ENV%" + - '%CMD_IN_ENV% nox -f bigquery\nox.py -e "%NOX_ENV%"' + - '%CMD_IN_ENV% nox -f bigtable\nox.py -e "%NOX_ENV%"' + - '%CMD_IN_ENV% nox -f core\nox.py -e "%NOX_ENV%"' + - '%CMD_IN_ENV% nox -f datastore\nox.py -e "%NOX_ENV%"' + - '%CMD_IN_ENV% nox -f dns\nox.py -e "%NOX_ENV%"' + - '%CMD_IN_ENV% nox -f error_reporting\nox.py -e "%NOX_ENV%"' + - '%CMD_IN_ENV% nox -f language\nox.py -e "%NOX_ENV%"' + - '%CMD_IN_ENV% nox -f logging\nox.py -e "%NOX_ENV%"' + - '%CMD_IN_ENV% nox -f monitoring\nox.py -e "%NOX_ENV%"' + - '%CMD_IN_ENV% nox -f pubsub\nox.py -e "%NOX_ENV%"' + - '%CMD_IN_ENV% nox -f resource_manager\nox.py -e "%NOX_ENV%"' + - '%CMD_IN_ENV% nox -f runtimeconfig\nox.py -e "%NOX_ENV%"' + - '%CMD_IN_ENV% nox -f spanner\nox.py -e "%NOX_ENV%"' + - '%CMD_IN_ENV% nox -f speech\nox.py -e "%NOX_ENV%"' + - '%CMD_IN_ENV% nox -f storage\nox.py -e "%NOX_ENV%"' + - '%CMD_IN_ENV% nox -f translate\nox.py -e "%NOX_ENV%"' + - '%CMD_IN_ENV% nox -f vision\nox.py -e "%NOX_ENV%"' after_test: # If tests are successful, create binary packages for the project. diff --git a/appveyor/requirements.txt b/appveyor/requirements.txt index 390d11078312..24cc58840e72 100644 --- a/appveyor/requirements.txt +++ b/appveyor/requirements.txt @@ -3,4 +3,4 @@ # pip will build them from source using the MSVC compiler matching the # target Python version and architecture wheel -tox +nox-automation==0.11.2 diff --git a/bigquery/.flake8 b/bigquery/.flake8 new file mode 100644 index 000000000000..25168dc87605 --- /dev/null +++ b/bigquery/.flake8 @@ -0,0 +1,6 @@ +[flake8] +exclude = + __pycache__, + .git, + *.pyc, + conf.py diff --git a/bigquery/LICENSE b/bigquery/LICENSE new file mode 100644 index 000000000000..d64569567334 --- /dev/null +++ b/bigquery/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed 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. diff --git a/bigquery/MANIFEST.in b/bigquery/MANIFEST.in index cb3a2b9ef4fa..24aa72fb370b 100644 --- a/bigquery/MANIFEST.in +++ b/bigquery/MANIFEST.in @@ -1,4 +1,3 @@ -include README.rst -graft google -graft unit_tests -global-exclude *.pyc +include README.rst LICENSE +recursive-include unit_tests * +global-exclude *.pyc __pycache__ diff --git a/bigquery/google/cloud/bigquery/__init__.py b/bigquery/google/cloud/bigquery/__init__.py index 615f7cfa1b6b..e2eb29e866a3 100644 --- a/bigquery/google/cloud/bigquery/__init__.py +++ b/bigquery/google/cloud/bigquery/__init__.py @@ -34,3 +34,9 @@ from google.cloud.bigquery.dataset import Dataset from google.cloud.bigquery.schema import SchemaField from google.cloud.bigquery.table import Table + +__all__ = [ + '__version__', 'AccessGrant', 'ArrayQueryParameter', 'Client', + 'Dataset', 'ScalarQueryParameter', 'SchemaField', 'StructQueryParameter', + 'Table', +] diff --git a/bigquery/nox.py b/bigquery/nox.py new file mode 100644 index 000000000000..74a7856d415e --- /dev/null +++ b/bigquery/nox.py @@ -0,0 +1,88 @@ +# Copyright 2016 Google Inc. +# +# Licensed 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. + +from __future__ import absolute_import + +import os + +import nox + + +@nox.session +@nox.parametrize('python_version', ['2.7', '3.4', '3.5', '3.6']) +def unit_tests(session, python_version): + """Run the unit test suite.""" + + # Run unit tests against all supported versions of Python. + session.interpreter = 'python%s' % python_version + + # Install all test dependencies, then install this package in-place. + session.install('mock', 'pytest', 'pytest-cov', '../core/') + session.install('-e', '.') + + # Run py.test against the unit tests. + session.run('py.test', '--quiet', + '--cov=google.cloud.bigquery', '--cov=tests.unit', '--cov-append', + '--cov-config=.coveragerc', '--cov-report=', '--cov-fail-under=97', + 'tests/unit', + ) + + +@nox.session +@nox.parametrize('python_version', ['2.7', '3.6']) +def system_tests(session, python_version): + """Run the system test suite.""" + + # Sanity check: Only run system tests if the environment variable is set. + if not os.environ.get('GOOGLE_APPLICATION_CREDENTIALS', ''): + return + + # Run the system tests against latest Python 2 and Python 3 only. + session.interpreter = 'python%s' % python_version + + # Install all test dependencies, then install this package into the + # virutalenv's dist-packages. + session.install('mock', 'pytest', + '../core/', '../test_utils/', + '../storage/') + session.install('.') + + # Run py.test against the system tests. + session.run('py.test', '--quiet', 'tests/system.py') + + +@nox.session +def lint(session): + """Run flake8. + + Returns a failure if flake8 finds linting errors or sufficiently + serious code quality issues. + """ + session.interpreter = 'python3.6' + session.install('flake8') + session.install('.') + session.run('flake8', 'google/cloud/bigquery') + + +@nox.session +def cover(session): + """Run the final coverage report. + + This outputs the coverage report aggregating coverage from the unit + test runs (not system test runs), and then erases coverage data. + """ + session.interpreter = 'python3.6' + session.install('coverage', 'pytest-cov') + session.run('coverage', 'report', '--show-missing', '--fail-under=100') + session.run('coverage', 'erase') diff --git a/bigquery/setup.py b/bigquery/setup.py index 1c7267d1f523..dd5ab25af9a1 100644 --- a/bigquery/setup.py +++ b/bigquery/setup.py @@ -62,7 +62,7 @@ 'google', 'google.cloud', ], - packages=find_packages(), + packages=find_packages(exclude=('unit_tests*',)), install_requires=REQUIREMENTS, **SETUP_BASE ) diff --git a/bigquery/tests/__init__.py b/bigquery/tests/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/system_tests/bigquery_test_data.json b/bigquery/tests/data/characters.json similarity index 100% rename from system_tests/bigquery_test_data.json rename to bigquery/tests/data/characters.json diff --git a/system_tests/bigquery_test_data.jsonl b/bigquery/tests/data/characters.jsonl similarity index 100% rename from system_tests/bigquery_test_data.jsonl rename to bigquery/tests/data/characters.jsonl diff --git a/system_tests/bigquery_test_schema.json b/bigquery/tests/data/schema.json similarity index 100% rename from system_tests/bigquery_test_schema.json rename to bigquery/tests/data/schema.json diff --git a/system_tests/bigquery.py b/bigquery/tests/system.py similarity index 99% rename from system_tests/bigquery.py rename to bigquery/tests/system.py index a1c70e37dfd3..a9e003fe968c 100644 --- a/system_tests/bigquery.py +++ b/bigquery/tests/system.py @@ -24,10 +24,10 @@ from google.cloud._helpers import UTC from google.cloud.exceptions import Forbidden -from retry import RetryErrors -from retry import RetryInstanceState -from retry import RetryResult -from system_test_utils import unique_resource_id +from test_utils.retry import RetryErrors +from test_utils.retry import RetryInstanceState +from test_utils.retry import RetryResult +from test_utils.system import unique_resource_id WHERE = os.path.abspath(os.path.dirname(__file__)) @@ -41,7 +41,7 @@ def _make_dataset_name(prefix): return '%s%s' % (prefix, unique_resource_id()) -def _load_json_schema(filename='bigquery_test_schema.json'): +def _load_json_schema(filename='data/schema.json'): from google.cloud.bigquery.table import _parse_schema_resource json_filename = os.path.join(WHERE, filename) @@ -839,7 +839,7 @@ def test_create_table_insert_fetch_nested_schema(self): to_insert = [] # Data is in "JSON Lines" format, see http://jsonlines.org/ - json_filename = os.path.join(WHERE, 'bigquery_test_data.jsonl') + json_filename = os.path.join(WHERE, 'data', 'characters.jsonl') with open(json_filename) as rows_file: for line in rows_file: mapping = json.loads(line) diff --git a/bigquery/unit_tests/__init__.py b/bigquery/tests/unit/__init__.py similarity index 100% rename from bigquery/unit_tests/__init__.py rename to bigquery/tests/unit/__init__.py diff --git a/bigquery/unit_tests/test__helpers.py b/bigquery/tests/unit/test__helpers.py similarity index 100% rename from bigquery/unit_tests/test__helpers.py rename to bigquery/tests/unit/test__helpers.py diff --git a/bigquery/unit_tests/test__http.py b/bigquery/tests/unit/test__http.py similarity index 100% rename from bigquery/unit_tests/test__http.py rename to bigquery/tests/unit/test__http.py diff --git a/bigquery/unit_tests/test_client.py b/bigquery/tests/unit/test_client.py similarity index 100% rename from bigquery/unit_tests/test_client.py rename to bigquery/tests/unit/test_client.py diff --git a/bigquery/unit_tests/test_dataset.py b/bigquery/tests/unit/test_dataset.py similarity index 100% rename from bigquery/unit_tests/test_dataset.py rename to bigquery/tests/unit/test_dataset.py diff --git a/bigquery/unit_tests/test_job.py b/bigquery/tests/unit/test_job.py similarity index 100% rename from bigquery/unit_tests/test_job.py rename to bigquery/tests/unit/test_job.py diff --git a/bigquery/unit_tests/test_query.py b/bigquery/tests/unit/test_query.py similarity index 100% rename from bigquery/unit_tests/test_query.py rename to bigquery/tests/unit/test_query.py diff --git a/bigquery/unit_tests/test_schema.py b/bigquery/tests/unit/test_schema.py similarity index 100% rename from bigquery/unit_tests/test_schema.py rename to bigquery/tests/unit/test_schema.py diff --git a/bigquery/unit_tests/test_table.py b/bigquery/tests/unit/test_table.py similarity index 100% rename from bigquery/unit_tests/test_table.py rename to bigquery/tests/unit/test_table.py diff --git a/bigquery/tox.ini b/bigquery/tox.ini deleted file mode 100644 index 2d142ba71df6..000000000000 --- a/bigquery/tox.ini +++ /dev/null @@ -1,35 +0,0 @@ -[tox] -envlist = - py27,py34,py35,cover - -[testing] -localdeps = - pip install --quiet --upgrade {toxinidir}/../core -deps = - {toxinidir}/../core - mock - pytest -covercmd = - py.test --quiet \ - --cov=google.cloud.bigquery \ - --cov=unit_tests \ - --cov-config {toxinidir}/.coveragerc \ - unit_tests - -[testenv] -commands = - {[testing]localdeps} - py.test --quiet {posargs} unit_tests -deps = - {[testing]deps} - -[testenv:cover] -basepython = - python2.7 -commands = - {[testing]localdeps} - {[testing]covercmd} -deps = - {[testenv]deps} - coverage - pytest-cov diff --git a/bigtable/.flake8 b/bigtable/.flake8 new file mode 100644 index 000000000000..5a380e53da40 --- /dev/null +++ b/bigtable/.flake8 @@ -0,0 +1,11 @@ +[flake8] +exclude = + # BigTable includes generated code in the manual layer; + # do not lint this. + google/cloud/bigtable/_generated/*.py, + + # Standard linting exemptions. + __pycache__, + .git, + *.pyc, + conf.py diff --git a/bigtable/LICENSE b/bigtable/LICENSE new file mode 100644 index 000000000000..d64569567334 --- /dev/null +++ b/bigtable/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed 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. diff --git a/bigtable/MANIFEST.in b/bigtable/MANIFEST.in index cb3a2b9ef4fa..9f7100c9528a 100644 --- a/bigtable/MANIFEST.in +++ b/bigtable/MANIFEST.in @@ -1,4 +1,4 @@ -include README.rst -graft google -graft unit_tests -global-exclude *.pyc +include README.rst LICENSE +recursive-include google *.json *.proto +recursive-include unit_tests * +global-exclude *.pyc __pycache__ diff --git a/bigtable/google/cloud/bigtable/__init__.py b/bigtable/google/cloud/bigtable/__init__.py index 0c815b8b0988..2886f24b67a1 100644 --- a/bigtable/google/cloud/bigtable/__init__.py +++ b/bigtable/google/cloud/bigtable/__init__.py @@ -19,3 +19,6 @@ __version__ = get_distribution('google-cloud-bigtable').version from google.cloud.bigtable.client import Client + + +__all__ = ['__version__', 'Client'] diff --git a/bigtable/nox.py b/bigtable/nox.py new file mode 100644 index 000000000000..eb8fe574c0c0 --- /dev/null +++ b/bigtable/nox.py @@ -0,0 +1,86 @@ +# Copyright 2016 Google Inc. +# +# Licensed 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. + +from __future__ import absolute_import +import os + +import nox + + +@nox.session +@nox.parametrize('python_version', ['2.7', '3.4', '3.5', '3.6']) +def unit_tests(session, python_version): + """Run the unit test suite.""" + + # Run unit tests against all supported versions of Python. + session.interpreter = 'python%s' % python_version + + # Install all test dependencies, then install this package in-place. + session.install('mock', 'pytest', 'pytest-cov', '../core/') + session.install('-e', '.') + + # Run py.test against the unit tests. + session.run('py.test', '--quiet', + '--cov=google.cloud.bigtable', '--cov=tests.unit', '--cov-append', + '--cov-config=.coveragerc', '--cov-report=', '--cov-fail-under=97', + 'tests/unit', + ) + + +@nox.session +@nox.parametrize('python_version', ['2.7', '3.6']) +def system_tests(session, python_version): + """Run the system test suite.""" + + # Sanity check: Only run system tests if the environment variable is set. + if not os.environ.get('GOOGLE_APPLICATION_CREDENTIALS', ''): + return + + # Run the system tests against latest Python 2 and Python 3 only. + session.interpreter = 'python%s' % python_version + + # Install all test dependencies, then install this package into the + # virutalenv's dist-packages. + session.install('mock', 'pytest', + '../core/', '../test_utils/') + session.install('.') + + # Run py.test against the system tests. + session.run('py.test', '--quiet', 'tests/system.py') + + +@nox.session +def lint(session): + """Run flake8. + + Returns a failure if flake8 finds linting errors or sufficiently + serious code quality issues. + """ + session.interpreter = 'python3.6' + session.install('flake8') + session.install('.') + session.run('flake8', 'google/cloud/bigtable') + + +@nox.session +def cover(session): + """Run the final coverage report. + + This outputs the coverage report aggregating coverage from the unit + test runs (not system test runs), and then erases coverage data. + """ + session.interpreter = 'python3.6' + session.install('coverage', 'pytest-cov') + session.run('coverage', 'report', '--show-missing', '--fail-under=100') + session.run('coverage', 'erase') diff --git a/bigtable/setup.py b/bigtable/setup.py index a7013bc4259c..b4fa4483490a 100644 --- a/bigtable/setup.py +++ b/bigtable/setup.py @@ -63,7 +63,7 @@ 'google', 'google.cloud', ], - packages=find_packages(), + packages=find_packages(exclude=('unit_tests*',)), install_requires=REQUIREMENTS, **SETUP_BASE ) diff --git a/bigtable/tests/__init__.py b/bigtable/tests/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/system_tests/bigtable.py b/bigtable/tests/system.py similarity index 99% rename from system_tests/bigtable.py rename to bigtable/tests/system.py index a57c1d781d93..faed85fdb302 100644 --- a/system_tests/bigtable.py +++ b/bigtable/tests/system.py @@ -31,10 +31,10 @@ from google.cloud.bigtable.row_data import PartialRowData from google.cloud.environment_vars import BIGTABLE_EMULATOR -from retry import RetryErrors -from retry import RetryResult -from system_test_utils import EmulatorCreds -from system_test_utils import unique_resource_id +from test_utils.retry import RetryErrors +from test_utils.retry import RetryResult +from test_utils.system import EmulatorCreds +from test_utils.system import unique_resource_id LOCATION_ID = 'us-central1-c' diff --git a/bigtable/unit_tests/__init__.py b/bigtable/tests/unit/__init__.py similarity index 100% rename from bigtable/unit_tests/__init__.py rename to bigtable/tests/unit/__init__.py diff --git a/bigtable/unit_tests/_testing.py b/bigtable/tests/unit/_testing.py similarity index 100% rename from bigtable/unit_tests/_testing.py rename to bigtable/tests/unit/_testing.py diff --git a/bigtable/unit_tests/read-rows-acceptance-test.json b/bigtable/tests/unit/read-rows-acceptance-test.json similarity index 100% rename from bigtable/unit_tests/read-rows-acceptance-test.json rename to bigtable/tests/unit/read-rows-acceptance-test.json diff --git a/bigtable/unit_tests/test_client.py b/bigtable/tests/unit/test_client.py similarity index 99% rename from bigtable/unit_tests/test_client.py rename to bigtable/tests/unit/test_client.py index 8761dc360c81..17656be60c00 100644 --- a/bigtable/unit_tests/test_client.py +++ b/bigtable/tests/unit/test_client.py @@ -543,7 +543,7 @@ def test_list_instances(self): instance_pb2 as data_v2_pb2) from google.cloud.bigtable._generated import ( bigtable_instance_admin_pb2 as messages_v2_pb2) - from unit_tests._testing import _FakeStub + from tests.unit._testing import _FakeStub LOCATION = 'projects/' + self.PROJECT + '/locations/locname' FAILED_LOCATION = 'FAILED' diff --git a/bigtable/unit_tests/test_cluster.py b/bigtable/tests/unit/test_cluster.py similarity index 98% rename from bigtable/unit_tests/test_cluster.py rename to bigtable/tests/unit/test_cluster.py index 9472fde29f59..3cc40964ba49 100644 --- a/bigtable/unit_tests/test_cluster.py +++ b/bigtable/tests/unit/test_cluster.py @@ -190,7 +190,7 @@ def test___ne__(self): self.assertNotEqual(cluster1, cluster2) def test_reload(self): - from unit_tests._testing import _FakeStub + from tests.unit._testing import _FakeStub from google.cloud.bigtable.cluster import DEFAULT_SERVE_NODES SERVE_NODES = 31 @@ -235,7 +235,7 @@ def test_create(self): from google.cloud.operation import Operation from google.cloud.bigtable._generated import ( bigtable_instance_admin_pb2 as messages_v2_pb2) - from unit_tests._testing import _FakeStub + from tests.unit._testing import _FakeStub SERVE_NODES = 4 client = _Client(self.PROJECT) @@ -285,7 +285,7 @@ def test_update(self): instance_pb2 as data_v2_pb2) from google.cloud.bigtable._generated import ( bigtable_instance_admin_pb2 as messages_v2_pb2) - from unit_tests._testing import _FakeStub + from tests.unit._testing import _FakeStub NOW = datetime.datetime.utcnow() NOW_PB = _datetime_to_pb_timestamp(NOW) @@ -345,7 +345,7 @@ def test_update(self): def test_delete(self): from google.protobuf import empty_pb2 - from unit_tests._testing import _FakeStub + from tests.unit._testing import _FakeStub client = _Client(self.PROJECT) instance = _Instance(self.INSTANCE_ID, client) diff --git a/bigtable/unit_tests/test_column_family.py b/bigtable/tests/unit/test_column_family.py similarity index 99% rename from bigtable/unit_tests/test_column_family.py rename to bigtable/tests/unit/test_column_family.py index 126a18da3003..6fa408fdb07e 100644 --- a/bigtable/unit_tests/test_column_family.py +++ b/bigtable/tests/unit/test_column_family.py @@ -352,7 +352,7 @@ def test_to_pb_with_rule(self): def _create_test_helper(self, gc_rule=None): from google.cloud.bigtable._generated import ( bigtable_table_admin_pb2 as table_admin_v2_pb2) - from unit_tests._testing import _FakeStub + from tests.unit._testing import _FakeStub project_id = 'project-id' zone = 'zone' @@ -409,7 +409,7 @@ def test_create_with_gc_rule(self): self._create_test_helper(gc_rule=gc_rule) def _update_test_helper(self, gc_rule=None): - from unit_tests._testing import _FakeStub + from tests.unit._testing import _FakeStub from google.cloud.bigtable._generated import ( bigtable_table_admin_pb2 as table_admin_v2_pb2) @@ -471,7 +471,7 @@ def test_delete(self): from google.protobuf import empty_pb2 from google.cloud.bigtable._generated import ( bigtable_table_admin_pb2 as table_admin_v2_pb2) - from unit_tests._testing import _FakeStub + from tests.unit._testing import _FakeStub project_id = 'project-id' zone = 'zone' diff --git a/bigtable/unit_tests/test_instance.py b/bigtable/tests/unit/test_instance.py similarity index 98% rename from bigtable/unit_tests/test_instance.py rename to bigtable/tests/unit/test_instance.py index aefba45d9158..cdad3c376d0a 100644 --- a/bigtable/unit_tests/test_instance.py +++ b/bigtable/tests/unit/test_instance.py @@ -192,7 +192,7 @@ def test_reload(self): instance_pb2 as data_v2_pb2) from google.cloud.bigtable._generated import ( bigtable_instance_admin_pb2 as messages_v2_pb) - from unit_tests._testing import _FakeStub + from tests.unit._testing import _FakeStub client = _Client(self.PROJECT) instance = self._make_one(self.INSTANCE_ID, client, self.LOCATION_ID) @@ -235,7 +235,7 @@ def test_create(self): from google.cloud.bigtable._generated import ( bigtable_instance_admin_pb2 as messages_v2_pb2) from google.cloud._helpers import _datetime_to_pb_timestamp - from unit_tests._testing import _FakeStub + from tests.unit._testing import _FakeStub from google.cloud.operation import Operation from google.cloud.bigtable.cluster import DEFAULT_SERVE_NODES @@ -290,7 +290,7 @@ def test_create_w_explicit_serve_nodes(self): from google.longrunning import operations_pb2 from google.cloud.bigtable._generated import ( bigtable_instance_admin_pb2 as messages_v2_pb2) - from unit_tests._testing import _FakeStub + from tests.unit._testing import _FakeStub from google.cloud.operation import Operation SERVE_NODES = 5 @@ -329,7 +329,7 @@ def test_create_w_explicit_serve_nodes(self): def test_update(self): from google.cloud.bigtable._generated import ( instance_pb2 as data_v2_pb2) - from unit_tests._testing import _FakeStub + from tests.unit._testing import _FakeStub client = _Client(self.PROJECT) instance = self._make_one(self.INSTANCE_ID, client, self.LOCATION_ID, @@ -364,7 +364,7 @@ def test_delete(self): from google.protobuf import empty_pb2 from google.cloud.bigtable._generated import ( bigtable_instance_admin_pb2 as messages_v2_pb) - from unit_tests._testing import _FakeStub + from tests.unit._testing import _FakeStub client = _Client(self.PROJECT) instance = self._make_one(self.INSTANCE_ID, client, self.LOCATION_ID) @@ -397,7 +397,7 @@ def test_list_clusters(self): instance_pb2 as instance_v2_pb2) from google.cloud.bigtable._generated import ( bigtable_instance_admin_pb2 as messages_v2_pb2) - from unit_tests._testing import _FakeStub + from tests.unit._testing import _FakeStub FAILED_LOCATION = 'FAILED' FAILED_LOCATIONS = [FAILED_LOCATION] @@ -454,7 +454,7 @@ def _list_tables_helper(self, table_name=None): table_pb2 as table_data_v2_pb2) from google.cloud.bigtable._generated import ( bigtable_table_admin_pb2 as table_messages_v1_pb2) - from unit_tests._testing import _FakeStub + from tests.unit._testing import _FakeStub client = _Client(self.PROJECT) instance = self._make_one(self.INSTANCE_ID, client, self.LOCATION_ID) diff --git a/bigtable/unit_tests/test_row.py b/bigtable/tests/unit/test_row.py similarity index 99% rename from bigtable/unit_tests/test_row.py rename to bigtable/tests/unit/test_row.py index 60d53d5ccdf6..3e2d4fd60e0f 100644 --- a/bigtable/unit_tests/test_row.py +++ b/bigtable/tests/unit/test_row.py @@ -301,7 +301,7 @@ def test_delete_cells_with_string_columns(self): def test_commit(self): from google.protobuf import empty_pb2 - from unit_tests._testing import _FakeStub + from tests.unit._testing import _FakeStub row_key = b'row_key' table_name = 'projects/more-stuff' @@ -361,7 +361,7 @@ def test_commit_too_many_mutations(self): row.commit() def test_commit_no_mutations(self): - from unit_tests._testing import _FakeStub + from tests.unit._testing import _FakeStub row_key = b'row_key' client = _Client() @@ -414,7 +414,7 @@ def test__get_mutations(self): self.assertIs(false_mutations, row._get_mutations(None)) def test_commit(self): - from unit_tests._testing import _FakeStub + from tests.unit._testing import _FakeStub from google.cloud.bigtable.row_filters import RowSampleFilter row_key = b'row_key' @@ -502,7 +502,7 @@ def test_commit_too_many_mutations(self): row.commit() def test_commit_no_mutations(self): - from unit_tests._testing import _FakeStub + from tests.unit._testing import _FakeStub row_key = b'row_key' client = _Client() @@ -582,7 +582,7 @@ def test_increment_cell_value(self): def test_commit(self): from google.cloud._testing import _Monkey - from unit_tests._testing import _FakeStub + from tests.unit._testing import _FakeStub from google.cloud.bigtable import row as MUT row_key = b'row_key' @@ -637,7 +637,7 @@ def mock_parse_rmw_row_response(row_response): self.assertEqual(row._rule_pb_list, []) def test_commit_no_rules(self): - from unit_tests._testing import _FakeStub + from tests.unit._testing import _FakeStub row_key = b'row_key' client = _Client() diff --git a/bigtable/unit_tests/test_row_data.py b/bigtable/tests/unit/test_row_data.py similarity index 100% rename from bigtable/unit_tests/test_row_data.py rename to bigtable/tests/unit/test_row_data.py diff --git a/bigtable/unit_tests/test_row_filters.py b/bigtable/tests/unit/test_row_filters.py similarity index 100% rename from bigtable/unit_tests/test_row_filters.py rename to bigtable/tests/unit/test_row_filters.py diff --git a/bigtable/unit_tests/test_table.py b/bigtable/tests/unit/test_table.py similarity index 98% rename from bigtable/unit_tests/test_table.py rename to bigtable/tests/unit/test_table.py index 4ad6afe1596f..63844f5d48b7 100644 --- a/bigtable/unit_tests/test_table.py +++ b/bigtable/tests/unit/test_table.py @@ -136,7 +136,7 @@ def test___ne__(self): def _create_test_helper(self, initial_split_keys, column_families=()): from google.cloud._helpers import _to_bytes - from unit_tests._testing import _FakeStub + from tests.unit._testing import _FakeStub client = _Client() instance = _Instance(self.INSTANCE_NAME, client=client) @@ -203,7 +203,7 @@ def test_create_with_column_families(self): column_families=column_families) def _list_column_families_helper(self): - from unit_tests._testing import _FakeStub + from tests.unit._testing import _FakeStub client = _Client() instance = _Instance(self.INSTANCE_NAME, client=client) @@ -241,7 +241,7 @@ def test_list_column_families(self): def test_delete(self): from google.protobuf import empty_pb2 - from unit_tests._testing import _FakeStub + from tests.unit._testing import _FakeStub client = _Client() instance = _Instance(self.INSTANCE_NAME, client=client) @@ -270,7 +270,7 @@ def test_delete(self): def _read_row_helper(self, chunks, expected_result): from google.cloud._testing import _Monkey - from unit_tests._testing import _FakeStub + from tests.unit._testing import _FakeStub from google.cloud.bigtable import table as MUT client = _Client() @@ -350,7 +350,7 @@ def test_read_row_still_partial(self): def test_read_rows(self): from google.cloud._testing import _Monkey - from unit_tests._testing import _FakeStub + from tests.unit._testing import _FakeStub from google.cloud.bigtable.row_data import PartialRowsData from google.cloud.bigtable import table as MUT @@ -400,7 +400,7 @@ def mock_create_row_request(table_name, **kwargs): self.assertEqual(mock_created, [(table.name, created_kwargs)]) def test_sample_row_keys(self): - from unit_tests._testing import _FakeStub + from tests.unit._testing import _FakeStub client = _Client() instance = _Instance(self.INSTANCE_NAME, client=client) diff --git a/bigtable/tox.ini b/bigtable/tox.ini deleted file mode 100644 index 257e21288b27..000000000000 --- a/bigtable/tox.ini +++ /dev/null @@ -1,35 +0,0 @@ -[tox] -envlist = - py27,py34,py35,cover - -[testing] -localdeps = - pip install --quiet --upgrade {toxinidir}/../core -deps = - {toxinidir}/../core - mock - pytest -covercmd = - py.test --quiet \ - --cov=google.cloud.bigtable \ - --cov=unit_tests \ - --cov-config {toxinidir}/.coveragerc \ - unit_tests - -[testenv] -commands = - {[testing]localdeps} - py.test --quiet {posargs} unit_tests -deps = - {[testing]deps} - -[testenv:cover] -basepython = - python2.7 -commands = - {[testing]localdeps} - {[testing]covercmd} -deps = - {[testenv]deps} - coverage - pytest-cov diff --git a/circle.yml b/circle.yml deleted file mode 100644 index 9ff6c2db10d2..000000000000 --- a/circle.yml +++ /dev/null @@ -1,32 +0,0 @@ -dependencies: - override: - - pip install --upgrade pip tox tox-pyenv - # latest as of Nov. 7, 2016: 2.7.12, 3.4.5, 3.5.2 - - pyenv local 2.7.12 3.4.4 3.5.2 - -test: - override: - - python2.7 scripts/run_unit_tests.py - - python3.4 scripts/run_unit_tests.py - - python3.5 scripts/run_unit_tests.py - - python scripts/run_unit_tests.py --tox-env cover - - tox -e lint - # - tox -e system-tests - # - tox -e system-tests3 - # - scripts/update_docs.sh - post: - - scripts/coveralls.sh - -general: - branches: - ignore: - - gh-pages - -deployment: - release: - # See "scripts/circleci_tagged_pkg.py" for info on REGEX - tag: /(([a-z]+)-)*([0-9]+)\.([0-9]+)\.([0-9]+)/ - owner: GoogleCloudPlatform - commands: - - pip install --upgrade twine - - ./scripts/circleci_twine_upload.sh diff --git a/core/.coveragerc b/core/.coveragerc index e72bb1216f10..9d89b1db5666 100644 --- a/core/.coveragerc +++ b/core/.coveragerc @@ -3,7 +3,9 @@ branch = True [report] omit = - */google/cloud/_testing.py + google/cloud/_testing.py + google/cloud/__init__.py + google/cloud/environment_vars.py fail_under = 100 show_missing = True exclude_lines = diff --git a/core/.flake8 b/core/.flake8 new file mode 100644 index 000000000000..25168dc87605 --- /dev/null +++ b/core/.flake8 @@ -0,0 +1,6 @@ +[flake8] +exclude = + __pycache__, + .git, + *.pyc, + conf.py diff --git a/core/LICENSE b/core/LICENSE new file mode 100644 index 000000000000..d64569567334 --- /dev/null +++ b/core/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed 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. diff --git a/core/MANIFEST.in b/core/MANIFEST.in index cb3a2b9ef4fa..24aa72fb370b 100644 --- a/core/MANIFEST.in +++ b/core/MANIFEST.in @@ -1,4 +1,3 @@ -include README.rst -graft google -graft unit_tests -global-exclude *.pyc +include README.rst LICENSE +recursive-include unit_tests * +global-exclude *.pyc __pycache__ diff --git a/core/nox.py b/core/nox.py new file mode 100644 index 000000000000..d8e068f3f00d --- /dev/null +++ b/core/nox.py @@ -0,0 +1,66 @@ +# Copyright 2016 Google Inc. +# +# Licensed 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. + +from __future__ import absolute_import + +import os + +import nox + + +@nox.session +@nox.parametrize('python_version', ['2.7', '3.4', '3.5', '3.6']) +def unit_tests(session, python_version): + """Run the unit test suite.""" + + # Run unit tests against all supported versions of Python. + session.interpreter = 'python%s' % python_version + + # Install all test dependencies, then install this package in-place. + session.install('mock', 'pytest', 'pytest-cov', + 'grpcio >= 1.0.2') + session.install('-e', '.') + + # Run py.test against the unit tests. + session.run('py.test', '--quiet', + '--cov=google.cloud', '--cov=tests.unit', '--cov-append', + '--cov-config=.coveragerc', '--cov-report=', '--cov-fail-under=97', + 'tests/unit', + ) + + +@nox.session +def lint(session): + """Run flake8. + + Returns a failure if flake8 finds linting errors or sufficiently + serious code quality issues. + """ + session.interpreter = 'python3.6' + session.install('flake8') + session.install('.') + session.run('flake8', 'google/cloud/core') + + +@nox.session +def cover(session): + """Run the final coverage report. + + This outputs the coverage report aggregating coverage from the unit + test runs (not system test runs), and then erases coverage data. + """ + session.interpreter = 'python3.6' + session.install('coverage', 'pytest-cov') + session.run('coverage', 'report', '--show-missing', '--fail-under=100') + session.run('coverage', 'erase') diff --git a/core/setup.py b/core/setup.py index 6ec06329bf76..73c00bf96de4 100644 --- a/core/setup.py +++ b/core/setup.py @@ -67,7 +67,7 @@ 'google', 'google.cloud', ], - packages=find_packages(), + packages=find_packages(exclude=('unit_tests*',)), install_requires=REQUIREMENTS, **SETUP_BASE ) diff --git a/core/tests/__init__.py b/core/tests/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/core/unit_tests/__init__.py b/core/tests/unit/__init__.py similarity index 100% rename from core/unit_tests/__init__.py rename to core/tests/unit/__init__.py diff --git a/core/unit_tests/streaming/__init__.py b/core/tests/unit/streaming/__init__.py similarity index 100% rename from core/unit_tests/streaming/__init__.py rename to core/tests/unit/streaming/__init__.py diff --git a/core/unit_tests/streaming/test_buffered_stream.py b/core/tests/unit/streaming/test_buffered_stream.py similarity index 100% rename from core/unit_tests/streaming/test_buffered_stream.py rename to core/tests/unit/streaming/test_buffered_stream.py diff --git a/core/unit_tests/streaming/test_exceptions.py b/core/tests/unit/streaming/test_exceptions.py similarity index 100% rename from core/unit_tests/streaming/test_exceptions.py rename to core/tests/unit/streaming/test_exceptions.py diff --git a/core/unit_tests/streaming/test_http_wrapper.py b/core/tests/unit/streaming/test_http_wrapper.py similarity index 100% rename from core/unit_tests/streaming/test_http_wrapper.py rename to core/tests/unit/streaming/test_http_wrapper.py diff --git a/core/unit_tests/streaming/test_stream_slice.py b/core/tests/unit/streaming/test_stream_slice.py similarity index 100% rename from core/unit_tests/streaming/test_stream_slice.py rename to core/tests/unit/streaming/test_stream_slice.py diff --git a/core/unit_tests/streaming/test_transfer.py b/core/tests/unit/streaming/test_transfer.py similarity index 100% rename from core/unit_tests/streaming/test_transfer.py rename to core/tests/unit/streaming/test_transfer.py diff --git a/core/unit_tests/streaming/test_util.py b/core/tests/unit/streaming/test_util.py similarity index 100% rename from core/unit_tests/streaming/test_util.py rename to core/tests/unit/streaming/test_util.py diff --git a/core/unit_tests/test__helpers.py b/core/tests/unit/test__helpers.py similarity index 100% rename from core/unit_tests/test__helpers.py rename to core/tests/unit/test__helpers.py diff --git a/core/unit_tests/test__http.py b/core/tests/unit/test__http.py similarity index 100% rename from core/unit_tests/test__http.py rename to core/tests/unit/test__http.py diff --git a/core/unit_tests/test_client.py b/core/tests/unit/test_client.py similarity index 100% rename from core/unit_tests/test_client.py rename to core/tests/unit/test_client.py diff --git a/core/unit_tests/test_credentials.py b/core/tests/unit/test_credentials.py similarity index 100% rename from core/unit_tests/test_credentials.py rename to core/tests/unit/test_credentials.py diff --git a/core/unit_tests/test_exceptions.py b/core/tests/unit/test_exceptions.py similarity index 100% rename from core/unit_tests/test_exceptions.py rename to core/tests/unit/test_exceptions.py diff --git a/core/unit_tests/test_iterator.py b/core/tests/unit/test_iterator.py similarity index 100% rename from core/unit_tests/test_iterator.py rename to core/tests/unit/test_iterator.py diff --git a/core/unit_tests/test_operation.py b/core/tests/unit/test_operation.py similarity index 100% rename from core/unit_tests/test_operation.py rename to core/tests/unit/test_operation.py diff --git a/core/tox.ini b/core/tox.ini deleted file mode 100644 index 156ffc07e00e..000000000000 --- a/core/tox.ini +++ /dev/null @@ -1,31 +0,0 @@ -[tox] -envlist = - py27,py34,py35,cover - -[testing] -deps = - grpcio >= 1.0.2 - mock - pytest -covercmd = - py.test --quiet \ - --cov=google.cloud \ - --cov=unit_tests \ - --cov-config {toxinidir}/.coveragerc \ - unit_tests - -[testenv] -commands = - py.test --quiet {posargs} unit_tests -deps = - {[testing]deps} - -[testenv:cover] -basepython = - python2.7 -commands = - {[testing]covercmd} -deps = - {[testenv]deps} - coverage - pytest-cov diff --git a/datastore/.flake8 b/datastore/.flake8 new file mode 100644 index 000000000000..25168dc87605 --- /dev/null +++ b/datastore/.flake8 @@ -0,0 +1,6 @@ +[flake8] +exclude = + __pycache__, + .git, + *.pyc, + conf.py diff --git a/datastore/LICENSE b/datastore/LICENSE new file mode 100644 index 000000000000..d64569567334 --- /dev/null +++ b/datastore/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed 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. diff --git a/datastore/MANIFEST.in b/datastore/MANIFEST.in index cb3a2b9ef4fa..9f7100c9528a 100644 --- a/datastore/MANIFEST.in +++ b/datastore/MANIFEST.in @@ -1,4 +1,4 @@ -include README.rst -graft google -graft unit_tests -global-exclude *.pyc +include README.rst LICENSE +recursive-include google *.json *.proto +recursive-include unit_tests * +global-exclude *.pyc __pycache__ diff --git a/datastore/google/cloud/datastore/__init__.py b/datastore/google/cloud/datastore/__init__.py index e09b2101e9ca..1cdc5db07ba3 100644 --- a/datastore/google/cloud/datastore/__init__.py +++ b/datastore/google/cloud/datastore/__init__.py @@ -63,3 +63,6 @@ from google.cloud.datastore.key import Key from google.cloud.datastore.query import Query from google.cloud.datastore.transaction import Transaction + +__all__ = ['__version__', 'Batch', 'Client', 'Entity', 'Key', 'Query', + 'Transaction'] diff --git a/datastore/google/cloud/datastore/client.py b/datastore/google/cloud/datastore/client.py index 2579fd85b097..ed2336a54b8a 100644 --- a/datastore/google/cloud/datastore/client.py +++ b/datastore/google/cloud/datastore/client.py @@ -526,7 +526,7 @@ def do_something(entity): .. testsetup:: query-page from google.cloud import datastore - from datastore import Config # system tests + from tests.system.test_system import Config # system tests client = datastore.Client() @@ -549,7 +549,7 @@ def do_something(entity): >>> first_page = next(pages) >>> first_page_entities = list(first_page) >>> query_iter.next_page_token - '...' + b'...' :type kwargs: dict :param kwargs: Parameters for initializing and instance of diff --git a/datastore/google/cloud/datastore/entity.py b/datastore/google/cloud/datastore/entity.py index 51fb0c659a7a..a1708c0ca8f6 100644 --- a/datastore/google/cloud/datastore/entity.py +++ b/datastore/google/cloud/datastore/entity.py @@ -43,7 +43,7 @@ class Entity(dict): .. testsetup:: entity-ctor from google.cloud import datastore - from datastore import Config # system tests + from tests.system.test_system import Config # system tests client = datastore.Client() key = client.key('EntityKind', 1234, namespace='_Doctest') @@ -56,7 +56,7 @@ class Entity(dict): .. doctest:: entity-ctor >>> client.get(key) - + You can the set values on the entity just like you would on any other dictionary. diff --git a/datastore/google/cloud/datastore/query.py b/datastore/google/cloud/datastore/query.py index ebbe72eed52a..726e3acc4920 100644 --- a/datastore/google/cloud/datastore/query.py +++ b/datastore/google/cloud/datastore/query.py @@ -58,7 +58,7 @@ class Query(object): (Optional) The namespace to which to restrict results. If not passed, uses the client's value. - :type ancestor: :class:`google.cloud.datastore.key.Key` + :type ancestor: :class:`~google.cloud.datastore.key.Key` :param ancestor: (Optional) key of the ancestor to which this query's results are restricted. @@ -173,7 +173,7 @@ def kind(self, value): def ancestor(self): """The ancestor key for the query. - :rtype: Key or None + :rtype: :class:`~google.cloud.datastore.key.Key` or None :returns: The ancestor for the query. """ return self._ancestor @@ -182,7 +182,7 @@ def ancestor(self): def ancestor(self, value): """Set the ancestor for the query - :type value: Key + :type value: :class:`~google.cloud.datastore.key.Key` :param value: the new ancestor key """ if not isinstance(value, Key): diff --git a/datastore/google/cloud/datastore/transaction.py b/datastore/google/cloud/datastore/transaction.py index 00d4ac1d891b..6108bd80647a 100644 --- a/datastore/google/cloud/datastore/transaction.py +++ b/datastore/google/cloud/datastore/transaction.py @@ -30,7 +30,7 @@ class Transaction(Batch): .. testsetup:: txn-put-multi, txn-api from google.cloud import datastore - from datastore import Config # system tests + from tests.system.test_system import Config # system tests client = datastore.Client() key1 = client.key('_Doctest') @@ -93,7 +93,7 @@ class SomeException(Exception): .. testsetup:: txn-entity-key, txn-entity-key-after, txn-manual from google.cloud import datastore - from datastore import Config # system tests + from tests.system.test_system import Config # system tests client = datastore.Client() diff --git a/datastore/nox.py b/datastore/nox.py new file mode 100644 index 000000000000..f724c18e5eef --- /dev/null +++ b/datastore/nox.py @@ -0,0 +1,110 @@ +# Copyright 2016 Google Inc. +# +# Licensed 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. + +from __future__ import absolute_import + +import os + +import nox + + +@nox.session +@nox.parametrize('python_version', ['2.7', '3.4', '3.5', '3.6']) +def unit_tests(session, python_version): + """Run the unit test suite.""" + + # Run unit tests against all supported versions of Python. + session.interpreter = 'python%s' % python_version + + # Install all test dependencies, then install this package in-place. + session.install('mock', 'pytest', 'pytest-cov', '../core/') + session.install('-e', '.') + + # Run py.test against the unit tests. + session.run('py.test', '--quiet', + '--cov=google.cloud.datastore', '--cov=tests.unit', '--cov-append', + '--cov-config=.coveragerc', '--cov-report=', '--cov-fail-under=97', + 'tests/unit', + ) + + +@nox.session +@nox.parametrize('python_version', ['2.7', '3.6']) +def system_tests(session, python_version): + """Run the system test suite.""" + + # Sanity check: Only run system tests if the environment variable is set. + if not os.environ.get('GOOGLE_APPLICATION_CREDENTIALS', ''): + return + + # Run the system tests against latest Python 2 and Python 3 only. + session.interpreter = 'python%s' % python_version + + # Install all test dependencies, then install this package into the + # virutalenv's dist-packages. + session.install('mock', 'pytest', '../core/', '../test_utils/') + session.install('.') + + # Run py.test against the system tests. + session.run('py.test', '--quiet', 'tests/system') + + +@nox.session +def doctests(session): + """Run the system test suite.""" + + # Sanity check: Only run system tests if the environment variable is set. + if not os.environ.get('GOOGLE_APPLICATION_CREDENTIALS', ''): + return + + # Doctests run against Python 3.6 only. + # It is difficult to make doctests run against both Python 2 and Python 3 + # because they test string output equivalence, which is difficult to + # make match (e.g. unicode literals starting with "u"). + session.interpreter = 'python3.6' + + # Install all test dependencies, then install this package into the + # virutalenv's dist-packages. + session.install('mock', 'pytest', 'sphinx', + '../core/', '../test_utils/') + session.install('.') + + # Run py.test against the system tests. + session.run('py.test', '--quiet', 'tests/doctests.py') + + +@nox.session +def lint(session): + """Run flake8. + + Returns a failure if flake8 finds linting errors or sufficiently + serious code quality issues. + """ + session.interpreter = 'python3.6' + session.install('flake8') + session.install('.') + session.run('flake8', 'google/cloud/datastore') + + +@nox.session +def cover(session): + """Run the final coverage report. + + This outputs the coverage report aggregating coverage from the unit + test runs (not system test runs), and then erases coverage data. + """ + session.interpreter = 'python3.6' + session.install('coverage', 'pytest-cov') + session.run('coverage', 'report', '--show-missing', '--fail-under=100') + session.run('coverage', 'erase') diff --git a/datastore/setup.py b/datastore/setup.py index 625415c65ddb..b8a5ec01d109 100644 --- a/datastore/setup.py +++ b/datastore/setup.py @@ -64,7 +64,7 @@ 'google', 'google.cloud', ], - packages=find_packages(), + packages=find_packages(exclude=('unit_tests*',)), install_requires=REQUIREMENTS, **SETUP_BASE ) diff --git a/datastore/tests/__init__.py b/datastore/tests/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/datastore/tests/doctests.py b/datastore/tests/doctests.py new file mode 100644 index 000000000000..5264635af03c --- /dev/null +++ b/datastore/tests/doctests.py @@ -0,0 +1,95 @@ +# Copyright 2014 Google Inc. +# +# Licensed 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. + +import os +import pkgutil +import tempfile +import unittest + +import six + +import sphinx + +from google.cloud import datastore + + +SPHINX_CONF = """\ +extensions = [ + 'sphinx.ext.autodoc', + 'sphinx.ext.doctest', +] +""" + +SPHINX_SECTION_TEMPLATE = """\ +Section %02d +=========== + +.. automodule:: google.cloud.%s + :members: + +""" + + +@unittest.skipIf(six.PY2, 'Doctests run against Python 3 only.') +class TestDoctest(unittest.TestCase): + + def _submodules(self): + pkg_iter = pkgutil.iter_modules(datastore.__path__) + result = [] + for _, mod_name, ispkg in pkg_iter: + self.assertFalse(ispkg) + result.append(mod_name) + + self.assertNotIn('__init__', result) + return result + + @staticmethod + def _add_section(index, mod_name, file_obj): + mod_part = 'datastore' + if mod_name != '__init__': + mod_part += '.' + mod_name + content = SPHINX_SECTION_TEMPLATE % (index, mod_part) + file_obj.write(content) + + def _make_temp_docs(self): + docs_dir = tempfile.mkdtemp(prefix='datastore-') + + conf_file = os.path.join(docs_dir, 'conf.py') + + with open(conf_file, 'w') as file_obj: + file_obj.write(SPHINX_CONF) + + index_file = os.path.join(docs_dir, 'contents.rst') + datastore_modules = self._submodules() + with open(index_file, 'w') as file_obj: + self._add_section(0, '__init__', file_obj) + for index, datastore_module in enumerate(datastore_modules): + self._add_section(index + 1, datastore_module, file_obj) + + return docs_dir + + def test_it(self): + from sphinx import application + + docs_dir = self._make_temp_docs() + outdir = os.path.join(docs_dir, 'doctest', 'out') + doctreedir = os.path.join(docs_dir, 'doctest', 'doctrees') + + app = application.Sphinx( + srcdir=docs_dir, confdir=docs_dir, + outdir=outdir, doctreedir=doctreedir, + buildername='doctest', warningiserror=True, parallel=1) + + app.build() + self.assertEqual(app.statuscode, 0) diff --git a/datastore/tests/system/__init__.py b/datastore/tests/system/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/system_tests/datastore.py b/datastore/tests/system/test_system.py similarity index 89% rename from system_tests/datastore.py rename to datastore/tests/system/test_system.py index c63dfb48de49..64d4b86fd007 100644 --- a/system_tests/datastore.py +++ b/datastore/tests/system/test_system.py @@ -14,8 +14,6 @@ import datetime import os -import pkgutil -import tempfile import unittest import httplib2 @@ -27,27 +25,11 @@ from google.cloud.environment_vars import GCD_DATASET from google.cloud.exceptions import Conflict -import clear_datastore -import populate_datastore -from system_test_utils import EmulatorCreds -from system_test_utils import unique_resource_id +from test_utils.system import EmulatorCreds +from test_utils.system import unique_resource_id - -SPHINX_CONF = """\ -extensions = [ - 'sphinx.ext.autodoc', - 'sphinx.ext.doctest', -] -""" - -SPHINX_SECTION_TEMPLATE = """\ -Section %02d -=========== - -.. automodule:: google.cloud.%s - :members: - -""" +from tests.system.utils import clear_datastore +from tests.system.utils import populate_datastore class Config(object): @@ -519,56 +501,3 @@ def test_failure_with_contention(self): # transaction. entity_in_txn[contention_prop_name] = u'inside' txn.put(entity_in_txn) - - -class TestDoctest(unittest.TestCase): - - def _submodules(self): - pkg_iter = pkgutil.iter_modules(datastore.__path__) - result = [] - for _, mod_name, ispkg in pkg_iter: - self.assertFalse(ispkg) - result.append(mod_name) - - self.assertNotIn('__init__', result) - return result - - @staticmethod - def _add_section(index, mod_name, file_obj): - mod_part = 'datastore' - if mod_name != '__init__': - mod_part += '.' + mod_name - content = SPHINX_SECTION_TEMPLATE % (index, mod_part) - file_obj.write(content) - - def _make_temp_docs(self): - docs_dir = tempfile.mkdtemp(prefix='datastore-') - - conf_file = os.path.join(docs_dir, 'conf.py') - - with open(conf_file, 'w') as file_obj: - file_obj.write(SPHINX_CONF) - - index_file = os.path.join(docs_dir, 'contents.rst') - datastore_modules = self._submodules() - with open(index_file, 'w') as file_obj: - self._add_section(0, '__init__', file_obj) - for index, datastore_module in enumerate(datastore_modules): - self._add_section(index + 1, datastore_module, file_obj) - - return docs_dir - - def test_it(self): - from sphinx import application - - docs_dir = self._make_temp_docs() - outdir = os.path.join(docs_dir, 'doctest', 'out') - doctreedir = os.path.join(docs_dir, 'doctest', 'doctrees') - - app = application.Sphinx( - srcdir=docs_dir, confdir=docs_dir, - outdir=outdir, doctreedir=doctreedir, - buildername='doctest', warningiserror=True, parallel=1) - - app.build() - self.assertEqual(app.statuscode, 0) diff --git a/datastore/tests/system/utils/__init__.py b/datastore/tests/system/utils/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/system_tests/clear_datastore.py b/datastore/tests/system/utils/clear_datastore.py similarity index 100% rename from system_tests/clear_datastore.py rename to datastore/tests/system/utils/clear_datastore.py diff --git a/system_tests/populate_datastore.py b/datastore/tests/system/utils/populate_datastore.py similarity index 100% rename from system_tests/populate_datastore.py rename to datastore/tests/system/utils/populate_datastore.py diff --git a/datastore/unit_tests/__init__.py b/datastore/tests/unit/__init__.py similarity index 100% rename from datastore/unit_tests/__init__.py rename to datastore/tests/unit/__init__.py diff --git a/datastore/unit_tests/test__gax.py b/datastore/tests/unit/test__gax.py similarity index 100% rename from datastore/unit_tests/test__gax.py rename to datastore/tests/unit/test__gax.py diff --git a/datastore/unit_tests/test__http.py b/datastore/tests/unit/test__http.py similarity index 100% rename from datastore/unit_tests/test__http.py rename to datastore/tests/unit/test__http.py diff --git a/datastore/unit_tests/test_batch.py b/datastore/tests/unit/test_batch.py similarity index 100% rename from datastore/unit_tests/test_batch.py rename to datastore/tests/unit/test_batch.py diff --git a/datastore/unit_tests/test_client.py b/datastore/tests/unit/test_client.py similarity index 100% rename from datastore/unit_tests/test_client.py rename to datastore/tests/unit/test_client.py diff --git a/datastore/unit_tests/test_entity.py b/datastore/tests/unit/test_entity.py similarity index 100% rename from datastore/unit_tests/test_entity.py rename to datastore/tests/unit/test_entity.py diff --git a/datastore/unit_tests/test_helpers.py b/datastore/tests/unit/test_helpers.py similarity index 100% rename from datastore/unit_tests/test_helpers.py rename to datastore/tests/unit/test_helpers.py diff --git a/datastore/unit_tests/test_key.py b/datastore/tests/unit/test_key.py similarity index 100% rename from datastore/unit_tests/test_key.py rename to datastore/tests/unit/test_key.py diff --git a/datastore/unit_tests/test_query.py b/datastore/tests/unit/test_query.py similarity index 100% rename from datastore/unit_tests/test_query.py rename to datastore/tests/unit/test_query.py diff --git a/datastore/unit_tests/test_transaction.py b/datastore/tests/unit/test_transaction.py similarity index 100% rename from datastore/unit_tests/test_transaction.py rename to datastore/tests/unit/test_transaction.py diff --git a/datastore/tox.ini b/datastore/tox.ini deleted file mode 100644 index 76ffb1f2a8b5..000000000000 --- a/datastore/tox.ini +++ /dev/null @@ -1,35 +0,0 @@ -[tox] -envlist = - py27,py34,py35,cover - -[testing] -localdeps = - pip install --quiet --upgrade {toxinidir}/../core -deps = - {toxinidir}/../core - mock - pytest -covercmd = - py.test --quiet \ - --cov=google.cloud.datastore \ - --cov=unit_tests \ - --cov-config {toxinidir}/.coveragerc \ - unit_tests - -[testenv] -commands = - {[testing]localdeps} - py.test --quiet {posargs} unit_tests -deps = - {[testing]deps} - -[testenv:cover] -basepython = - python2.7 -commands = - {[testing]localdeps} - {[testing]covercmd} -deps = - {[testenv]deps} - coverage - pytest-cov diff --git a/dns/.flake8 b/dns/.flake8 new file mode 100644 index 000000000000..25168dc87605 --- /dev/null +++ b/dns/.flake8 @@ -0,0 +1,6 @@ +[flake8] +exclude = + __pycache__, + .git, + *.pyc, + conf.py diff --git a/dns/LICENSE b/dns/LICENSE new file mode 100644 index 000000000000..d64569567334 --- /dev/null +++ b/dns/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed 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. diff --git a/dns/MANIFEST.in b/dns/MANIFEST.in index cb3a2b9ef4fa..9f7100c9528a 100644 --- a/dns/MANIFEST.in +++ b/dns/MANIFEST.in @@ -1,4 +1,4 @@ -include README.rst -graft google -graft unit_tests -global-exclude *.pyc +include README.rst LICENSE +recursive-include google *.json *.proto +recursive-include unit_tests * +global-exclude *.pyc __pycache__ diff --git a/dns/google/cloud/dns/__init__.py b/dns/google/cloud/dns/__init__.py index b836fa86b2a8..1136e6cb743f 100644 --- a/dns/google/cloud/dns/__init__.py +++ b/dns/google/cloud/dns/__init__.py @@ -35,3 +35,7 @@ SCOPE = Client.SCOPE + + +__all__ = ['__version__', 'Changes', 'Client', 'ManagedZone', + 'ResourceRecordSet', 'SCOPE'] diff --git a/dns/nox.py b/dns/nox.py new file mode 100644 index 000000000000..f582603fc3f0 --- /dev/null +++ b/dns/nox.py @@ -0,0 +1,65 @@ +# Copyright 2016 Google Inc. +# +# Licensed 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. + +from __future__ import absolute_import + +import os + +import nox + + +@nox.session +@nox.parametrize('python_version', ['2.7', '3.4', '3.5', '3.6']) +def unit_tests(session, python_version): + """Run the unit test suite.""" + + # Run unit tests against all supported versions of Python. + session.interpreter = 'python%s' % python_version + + # Install all test dependencies, then install this package in-place. + session.install('mock', 'pytest', 'pytest-cov', '../core/') + session.install('-e', '.') + + # Run py.test against the unit tests. + session.run('py.test', '--quiet', + '--cov=google.cloud.dns', '--cov=tests.unit', '--cov-append', + '--cov-config=.coveragerc', '--cov-report=', '--cov-fail-under=97', + 'tests/unit', + ) + + +@nox.session +def lint(session): + """Run flake8. + + Returns a failure if flake8 finds linting errors or sufficiently + serious code quality issues. + """ + session.interpreter = 'python3.6' + session.install('flake8') + session.install('.') + session.run('flake8', 'google/cloud/dns') + + +@nox.session +def cover(session): + """Run the final coverage report. + + This outputs the coverage report aggregating coverage from the unit + test runs (not system test runs), and then erases coverage data. + """ + session.interpreter = 'python3.6' + session.install('coverage', 'pytest-cov') + session.run('coverage', 'report', '--show-missing', '--fail-under=100') + session.run('coverage', 'erase') diff --git a/dns/setup.py b/dns/setup.py index 7078dc328b0d..19bb70c8df9b 100644 --- a/dns/setup.py +++ b/dns/setup.py @@ -62,7 +62,7 @@ 'google', 'google.cloud', ], - packages=find_packages(), + packages=find_packages(exclude=('unit_tests*',)), install_requires=REQUIREMENTS, **SETUP_BASE ) diff --git a/dns/tests/__init__.py b/dns/tests/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/dns/unit_tests/__init__.py b/dns/tests/unit/__init__.py similarity index 100% rename from dns/unit_tests/__init__.py rename to dns/tests/unit/__init__.py diff --git a/dns/unit_tests/test__http.py b/dns/tests/unit/test__http.py similarity index 100% rename from dns/unit_tests/test__http.py rename to dns/tests/unit/test__http.py diff --git a/dns/unit_tests/test_changes.py b/dns/tests/unit/test_changes.py similarity index 100% rename from dns/unit_tests/test_changes.py rename to dns/tests/unit/test_changes.py diff --git a/dns/unit_tests/test_client.py b/dns/tests/unit/test_client.py similarity index 100% rename from dns/unit_tests/test_client.py rename to dns/tests/unit/test_client.py diff --git a/dns/unit_tests/test_resource_record_set.py b/dns/tests/unit/test_resource_record_set.py similarity index 100% rename from dns/unit_tests/test_resource_record_set.py rename to dns/tests/unit/test_resource_record_set.py diff --git a/dns/unit_tests/test_zone.py b/dns/tests/unit/test_zone.py similarity index 100% rename from dns/unit_tests/test_zone.py rename to dns/tests/unit/test_zone.py diff --git a/dns/tox.ini b/dns/tox.ini deleted file mode 100644 index f4dd23f414cb..000000000000 --- a/dns/tox.ini +++ /dev/null @@ -1,35 +0,0 @@ -[tox] -envlist = - py27,py34,py35,cover - -[testing] -localdeps = - pip install --quiet --upgrade {toxinidir}/../core -deps = - {toxinidir}/../core - mock - pytest -covercmd = - py.test --quiet \ - --cov=google.cloud.dns \ - --cov=unit_tests \ - --cov-config {toxinidir}/.coveragerc \ - unit_tests - -[testenv] -commands = - {[testing]localdeps} - py.test --quiet {posargs} unit_tests -deps = - {[testing]deps} - -[testenv:cover] -basepython = - python2.7 -commands = - {[testing]localdeps} - {[testing]covercmd} -deps = - {[testenv]deps} - coverage - pytest-cov diff --git a/error_reporting/.flake8 b/error_reporting/.flake8 new file mode 100644 index 000000000000..25168dc87605 --- /dev/null +++ b/error_reporting/.flake8 @@ -0,0 +1,6 @@ +[flake8] +exclude = + __pycache__, + .git, + *.pyc, + conf.py diff --git a/error_reporting/LICENSE b/error_reporting/LICENSE new file mode 100644 index 000000000000..d64569567334 --- /dev/null +++ b/error_reporting/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed 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. diff --git a/error_reporting/MANIFEST.in b/error_reporting/MANIFEST.in index cb3a2b9ef4fa..9f7100c9528a 100644 --- a/error_reporting/MANIFEST.in +++ b/error_reporting/MANIFEST.in @@ -1,4 +1,4 @@ -include README.rst -graft google -graft unit_tests -global-exclude *.pyc +include README.rst LICENSE +recursive-include google *.json *.proto +recursive-include unit_tests * +global-exclude *.pyc __pycache__ diff --git a/error_reporting/nox.py b/error_reporting/nox.py new file mode 100644 index 000000000000..10161156d921 --- /dev/null +++ b/error_reporting/nox.py @@ -0,0 +1,65 @@ +# Copyright 2016 Google Inc. +# +# Licensed 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. + +from __future__ import absolute_import + +import os + +import nox + + +@nox.session +@nox.parametrize('python_version', ['2.7', '3.4', '3.5', '3.6']) +def unit_tests(session, python_version): + """Run the unit test suite.""" + + # Run unit tests against all supported versions of Python. + session.interpreter = 'python%s' % python_version + + # Install all test dependencies, then install this package in-place. + session.install('mock', 'pytest', 'pytest-cov', '../core/') + session.install('-e', '.') + + # Run py.test against the unit tests. + session.run('py.test', '--quiet', + '--cov=google.cloud.datastore', '--cov=tests.unit', '--cov-append', + '--cov-config=.coveragerc', '--cov-report=', '--cov-fail-under=97', + 'tests/unit', + ) + + +@nox.session +def lint(session): + """Run flake8. + + Returns a failure if flake8 finds linting errors or sufficiently + serious code quality issues. + """ + session.interpreter = 'python3.6' + session.install('flake8') + session.install('.') + session.run('flake8', 'google/cloud/datastore') + + +@nox.session +def cover(session): + """Run the final coverage report. + + This outputs the coverage report aggregating coverage from the unit + test runs (not system test runs), and then erases coverage data. + """ + session.interpreter = 'python3.6' + session.install('coverage', 'pytest-cov') + session.run('coverage', 'report', '--show-missing', '--fail-under=100') + session.run('coverage', 'erase') diff --git a/error_reporting/setup.py b/error_reporting/setup.py index e8086d324277..106ac3ff5976 100644 --- a/error_reporting/setup.py +++ b/error_reporting/setup.py @@ -64,7 +64,7 @@ 'google', 'google.cloud', ], - packages=find_packages(), + packages=find_packages(exclude=('unit_tests*',)), install_requires=REQUIREMENTS, **SETUP_BASE ) diff --git a/error_reporting/tests/__init__.py b/error_reporting/tests/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/error_reporting/unit_tests/__init__.py b/error_reporting/tests/unit/__init__.py similarity index 100% rename from error_reporting/unit_tests/__init__.py rename to error_reporting/tests/unit/__init__.py diff --git a/error_reporting/unit_tests/test__gax.py b/error_reporting/tests/unit/test__gax.py similarity index 100% rename from error_reporting/unit_tests/test__gax.py rename to error_reporting/tests/unit/test__gax.py diff --git a/error_reporting/unit_tests/test__logging.py b/error_reporting/tests/unit/test__logging.py similarity index 100% rename from error_reporting/unit_tests/test__logging.py rename to error_reporting/tests/unit/test__logging.py diff --git a/error_reporting/unit_tests/test_client.py b/error_reporting/tests/unit/test_client.py similarity index 100% rename from error_reporting/unit_tests/test_client.py rename to error_reporting/tests/unit/test_client.py diff --git a/error_reporting/unit_tests/test_util.py b/error_reporting/tests/unit/test_util.py similarity index 100% rename from error_reporting/unit_tests/test_util.py rename to error_reporting/tests/unit/test_util.py diff --git a/error_reporting/tox.ini b/error_reporting/tox.ini deleted file mode 100644 index 63c0e63bb2cd..000000000000 --- a/error_reporting/tox.ini +++ /dev/null @@ -1,38 +0,0 @@ -[tox] -envlist = - py27,py34,py35,cover - -[testing] -localdeps = - pip install --quiet --upgrade \ - {toxinidir}/../core \ - {toxinidir}/../logging -deps = - {toxinidir}/../core - {toxinidir}/../logging - mock - pytest -covercmd = - py.test --quiet \ - --cov=google.cloud.error_reporting \ - --cov=unit_tests \ - --cov-config {toxinidir}/.coveragerc \ - unit_tests - -[testenv] -commands = - {[testing]localdeps} - py.test --quiet {posargs} unit_tests -deps = - {[testing]deps} - -[testenv:cover] -basepython = - python2.7 -commands = - {[testing]localdeps} - {[testing]covercmd} -deps = - {[testenv]deps} - coverage - pytest-cov diff --git a/language/.flake8 b/language/.flake8 new file mode 100644 index 000000000000..25168dc87605 --- /dev/null +++ b/language/.flake8 @@ -0,0 +1,6 @@ +[flake8] +exclude = + __pycache__, + .git, + *.pyc, + conf.py diff --git a/language/LICENSE b/language/LICENSE new file mode 100644 index 000000000000..d64569567334 --- /dev/null +++ b/language/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed 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. diff --git a/language/MANIFEST.in b/language/MANIFEST.in index cb3a2b9ef4fa..9f7100c9528a 100644 --- a/language/MANIFEST.in +++ b/language/MANIFEST.in @@ -1,4 +1,4 @@ -include README.rst -graft google -graft unit_tests -global-exclude *.pyc +include README.rst LICENSE +recursive-include google *.json *.proto +recursive-include unit_tests * +global-exclude *.pyc __pycache__ diff --git a/language/google/cloud/language/__init__.py b/language/google/cloud/language/__init__.py index 9519b930c333..8cc584b17cb8 100644 --- a/language/google/cloud/language/__init__.py +++ b/language/google/cloud/language/__init__.py @@ -21,3 +21,5 @@ from google.cloud.language.client import Client from google.cloud.language.document import Document from google.cloud.language.document import Encoding + +__all__ = ['Client', 'Document', 'Encoding', '__version__'] diff --git a/language/nox.py b/language/nox.py new file mode 100644 index 000000000000..9e61bc897263 --- /dev/null +++ b/language/nox.py @@ -0,0 +1,87 @@ +# Copyright 2016 Google Inc. +# +# Licensed 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. + +from __future__ import absolute_import + +import os + +import nox + + +@nox.session +@nox.parametrize('python_version', ['2.7', '3.4', '3.5', '3.6']) +def unit_tests(session, python_version): + """Run the unit test suite.""" + + # Run unit tests against all supported versions of Python. + session.interpreter = 'python%s' % python_version + + # Install all test dependencies, then install this package in-place. + session.install('mock', 'pytest', 'pytest-cov', '../core/') + session.install('-e', '.') + + # Run py.test against the unit tests. + session.run('py.test', '--quiet', + '--cov=google.cloud.language', '--cov=tests.unit', '--cov-append', + '--cov-config=.coveragerc', '--cov-report=', '--cov-fail-under=97', + 'tests/unit', + ) + + +@nox.session +@nox.parametrize('python_version', ['2.7', '3.6']) +def system_tests(session, python_version): + """Run the system test suite.""" + + # Sanity check: Only run system tests if the environment variable is set. + if not os.environ.get('GOOGLE_APPLICATION_CREDENTIALS', ''): + return + + # Run the system tests against latest Python 2 and Python 3 only. + session.interpreter = 'python%s' % python_version + + # Install all test dependencies, then install this package into the + # virutalenv's dist-packages. + session.install('mock', 'pytest', + '../core/', '../storage/', '../test_utils/') + session.install('.') + + # Run py.test against the system tests. + session.run('py.test', '--quiet', 'tests/system.py') + + +@nox.session +def lint(session): + """Run flake8. + + Returns a failure if flake8 finds linting errors or sufficiently + serious code quality issues. + """ + session.interpreter = 'python3.6' + session.install('flake8') + session.install('.') + session.run('flake8', 'google/cloud/language') + + +@nox.session +def cover(session): + """Run the final coverage report. + + This outputs the coverage report aggregating coverage from the unit + test runs (not system test runs), and then erases coverage data. + """ + session.interpreter = 'python3.6' + session.install('coverage', 'pytest-cov') + session.run('coverage', 'report', '--show-missing', '--fail-under=100') + session.run('coverage', 'erase') diff --git a/language/setup.py b/language/setup.py index a5d62474ce4c..ba836831d154 100644 --- a/language/setup.py +++ b/language/setup.py @@ -62,7 +62,7 @@ 'google', 'google.cloud', ], - packages=find_packages(), + packages=find_packages(exclude=('unit_tests*',)), install_requires=REQUIREMENTS, **SETUP_BASE ) diff --git a/language/tests/__init__.py b/language/tests/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/system_tests/language.py b/language/tests/system.py similarity index 98% rename from system_tests/language.py rename to language/tests/system.py index ac56799f36d8..5802744762fa 100644 --- a/system_tests/language.py +++ b/language/tests/system.py @@ -18,8 +18,8 @@ from google.cloud import language from google.cloud import storage -from system_test_utils import unique_resource_id -from retry import RetryErrors +from test_utils.system import unique_resource_id +from test_utils.retry import RetryErrors class Config(object): diff --git a/language/unit_tests/__init__.py b/language/tests/unit/__init__.py similarity index 100% rename from language/unit_tests/__init__.py rename to language/tests/unit/__init__.py diff --git a/language/unit_tests/test__http.py b/language/tests/unit/test__http.py similarity index 100% rename from language/unit_tests/test__http.py rename to language/tests/unit/test__http.py diff --git a/language/unit_tests/test_api_responses.py b/language/tests/unit/test_api_responses.py similarity index 100% rename from language/unit_tests/test_api_responses.py rename to language/tests/unit/test_api_responses.py diff --git a/language/unit_tests/test_client.py b/language/tests/unit/test_client.py similarity index 100% rename from language/unit_tests/test_client.py rename to language/tests/unit/test_client.py diff --git a/language/unit_tests/test_document.py b/language/tests/unit/test_document.py similarity index 100% rename from language/unit_tests/test_document.py rename to language/tests/unit/test_document.py diff --git a/language/unit_tests/test_entity.py b/language/tests/unit/test_entity.py similarity index 100% rename from language/unit_tests/test_entity.py rename to language/tests/unit/test_entity.py diff --git a/language/unit_tests/test_sentence.py b/language/tests/unit/test_sentence.py similarity index 100% rename from language/unit_tests/test_sentence.py rename to language/tests/unit/test_sentence.py diff --git a/language/unit_tests/test_sentiment.py b/language/tests/unit/test_sentiment.py similarity index 100% rename from language/unit_tests/test_sentiment.py rename to language/tests/unit/test_sentiment.py diff --git a/language/unit_tests/test_syntax.py b/language/tests/unit/test_syntax.py similarity index 100% rename from language/unit_tests/test_syntax.py rename to language/tests/unit/test_syntax.py diff --git a/language/tox.ini b/language/tox.ini deleted file mode 100644 index 3f9f18cb23ea..000000000000 --- a/language/tox.ini +++ /dev/null @@ -1,35 +0,0 @@ -[tox] -envlist = - py27,py34,py35,cover - -[testing] -localdeps = - pip install --quiet --upgrade {toxinidir}/../core -deps = - {toxinidir}/../core - mock - pytest -covercmd = - py.test --quiet \ - --cov=google.cloud.language \ - --cov=unit_tests \ - --cov-config {toxinidir}/.coveragerc \ - unit_tests - -[testenv] -commands = - {[testing]localdeps} - py.test --quiet {posargs} unit_tests -deps = - {[testing]deps} - -[testenv:cover] -basepython = - python2.7 -commands = - {[testing]localdeps} - {[testing]covercmd} -deps = - {[testing]deps} - coverage - pytest-cov diff --git a/logging/.flake8 b/logging/.flake8 new file mode 100644 index 000000000000..25168dc87605 --- /dev/null +++ b/logging/.flake8 @@ -0,0 +1,6 @@ +[flake8] +exclude = + __pycache__, + .git, + *.pyc, + conf.py diff --git a/logging/LICENSE b/logging/LICENSE new file mode 100644 index 000000000000..d64569567334 --- /dev/null +++ b/logging/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed 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. diff --git a/logging/MANIFEST.in b/logging/MANIFEST.in index cb3a2b9ef4fa..9f7100c9528a 100644 --- a/logging/MANIFEST.in +++ b/logging/MANIFEST.in @@ -1,4 +1,4 @@ -include README.rst -graft google -graft unit_tests -global-exclude *.pyc +include README.rst LICENSE +recursive-include google *.json *.proto +recursive-include unit_tests * +global-exclude *.pyc __pycache__ diff --git a/logging/google/cloud/logging/__init__.py b/logging/google/cloud/logging/__init__.py index 17df46b08bad..cced78370c6a 100644 --- a/logging/google/cloud/logging/__init__.py +++ b/logging/google/cloud/logging/__init__.py @@ -25,3 +25,5 @@ """Query string to order by ascending timestamps.""" DESCENDING = 'timestamp desc' """Query string to order by decending timestamps.""" + +__all__ = ['__version__', 'ASCENDING', 'Client', 'DESCENDING'] diff --git a/logging/google/cloud/logging/handlers/__init__.py b/logging/google/cloud/logging/handlers/__init__.py index 9745296e9782..432419543bea 100644 --- a/logging/google/cloud/logging/handlers/__init__.py +++ b/logging/google/cloud/logging/handlers/__init__.py @@ -19,3 +19,6 @@ ContainerEngineHandler) from google.cloud.logging.handlers.handlers import CloudLoggingHandler from google.cloud.logging.handlers.handlers import setup_logging + +__all__ = ['AppEngineHandler', 'CloudLoggingHandler', 'ContainerEngineHandler', + 'setup_logging'] diff --git a/logging/google/cloud/logging/handlers/transports/__init__.py b/logging/google/cloud/logging/handlers/transports/__init__.py index 6c689e378a42..b1091b70788d 100644 --- a/logging/google/cloud/logging/handlers/transports/__init__.py +++ b/logging/google/cloud/logging/handlers/transports/__init__.py @@ -24,3 +24,5 @@ from google.cloud.logging.handlers.transports.sync import SyncTransport from google.cloud.logging.handlers.transports.background_thread import ( BackgroundThreadTransport) + +__all__ = ['BackgroundThreadTransport', 'SyncTransport', 'Transport'] diff --git a/logging/nox.py b/logging/nox.py new file mode 100644 index 000000000000..59e85f72b4a9 --- /dev/null +++ b/logging/nox.py @@ -0,0 +1,88 @@ +# Copyright 2016 Google Inc. +# +# Licensed 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. + +from __future__ import absolute_import + +import os + +import nox + + +@nox.session +@nox.parametrize('python_version', ['2.7', '3.4', '3.5', '3.6']) +def unit_tests(session, python_version): + """Run the unit test suite.""" + + # Run unit tests against all supported versions of Python. + session.interpreter = 'python%s' % python_version + + # Install all test dependencies, then install this package in-place. + session.install('mock', 'pytest', 'pytest-cov', '../core/') + session.install('-e', '.') + + # Run py.test against the unit tests. + session.run('py.test', '--quiet', + '--cov=google.cloud.logging', '--cov=tests.unit', '--cov-append', + '--cov-config=.coveragerc', '--cov-report=', '--cov-fail-under=97', + 'tests/unit', + ) + + +@nox.session +@nox.parametrize('python_version', ['2.7', '3.6']) +def system_tests(session, python_version): + """Run the system test suite.""" + + # Sanity check: Only run system tests if the environment variable is set. + if not os.environ.get('GOOGLE_APPLICATION_CREDENTIALS', ''): + return + + # Run the system tests against latest Python 2 and Python 3 only. + session.interpreter = 'python%s' % python_version + + # Install all test dependencies, then install this package into the + # virutalenv's dist-packages. + session.install('mock', 'pytest', + '../core/', '../test_utils/', + '../bigquery/', '../pubsub/', '../storage/') + session.install('.') + + # Run py.test against the system tests. + session.run('py.test', '--quiet', 'tests/system.py') + + +@nox.session +def lint(session): + """Run flake8. + + Returns a failure if flake8 finds linting errors or sufficiently + serious code quality issues. + """ + session.interpreter = 'python3.6' + session.install('flake8') + session.install('.') + session.run('flake8', 'google/cloud/logging') + + +@nox.session +def cover(session): + """Run the final coverage report. + + This outputs the coverage report aggregating coverage from the unit + test runs (not system test runs), and then erases coverage data. + """ + session.interpreter = 'python3.6' + session.install('coverage', 'pytest-cov') + session.run('coverage', 'report', '--show-missing', '--fail-under=100') + session.run('coverage', 'erase') diff --git a/logging/setup.py b/logging/setup.py index d56b15995c71..87a4e66fe15e 100644 --- a/logging/setup.py +++ b/logging/setup.py @@ -64,7 +64,7 @@ 'google', 'google.cloud', ], - packages=find_packages(), + packages=find_packages(exclude=('unit_tests*',)), install_requires=REQUIREMENTS, **SETUP_BASE ) diff --git a/logging/tests/__init__.py b/logging/tests/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/system_tests/logging_.py b/logging/tests/system.py similarity index 98% rename from system_tests/logging_.py rename to logging/tests/system.py index 52229d98ffb5..f564ec3dcbdf 100644 --- a/system_tests/logging_.py +++ b/logging/tests/system.py @@ -30,9 +30,9 @@ from google.cloud.logging.handlers.transports import SyncTransport from google.cloud.logging import client -from retry import RetryErrors -from retry import RetryResult -from system_test_utils import unique_resource_id +from test_utils.retry import RetryErrors +from test_utils.retry import RetryResult +from test_utils.system import unique_resource_id _RESOURCE_ID = unique_resource_id('-') DEFAULT_FILTER = 'logName:syslog AND severity>=INFO' @@ -114,7 +114,7 @@ def setUp(self): self._handlers_cache = logging.getLogger().handlers[:] def tearDown(self): - retry = RetryErrors(NotFound) + retry = RetryErrors(NotFound, max_tries=10) for doomed in self.to_delete: retry(doomed.delete)() logging.getLogger().handlers = self._handlers_cache[:] @@ -447,7 +447,7 @@ def test_reload_sink(self): def test_update_sink(self): SINK_NAME = 'test-update-sink%s' % (_RESOURCE_ID,) - retry = RetryErrors(Conflict) + retry = RetryErrors(Conflict, max_tries=10) bucket_uri = self._init_storage_bucket() dataset_uri = self._init_bigquery_dataset() UPDATED_FILTER = 'logName:syslog' diff --git a/logging/unit_tests/__init__.py b/logging/tests/unit/__init__.py similarity index 100% rename from logging/unit_tests/__init__.py rename to logging/tests/unit/__init__.py diff --git a/logging/unit_tests/handlers/__init__.py b/logging/tests/unit/handlers/__init__.py similarity index 100% rename from logging/unit_tests/handlers/__init__.py rename to logging/tests/unit/handlers/__init__.py diff --git a/logging/unit_tests/handlers/test_app_engine.py b/logging/tests/unit/handlers/test_app_engine.py similarity index 100% rename from logging/unit_tests/handlers/test_app_engine.py rename to logging/tests/unit/handlers/test_app_engine.py diff --git a/logging/unit_tests/handlers/test_container_engine.py b/logging/tests/unit/handlers/test_container_engine.py similarity index 100% rename from logging/unit_tests/handlers/test_container_engine.py rename to logging/tests/unit/handlers/test_container_engine.py diff --git a/logging/unit_tests/handlers/test_handlers.py b/logging/tests/unit/handlers/test_handlers.py similarity index 100% rename from logging/unit_tests/handlers/test_handlers.py rename to logging/tests/unit/handlers/test_handlers.py diff --git a/logging/unit_tests/handlers/transports/__init__.py b/logging/tests/unit/handlers/transports/__init__.py similarity index 100% rename from logging/unit_tests/handlers/transports/__init__.py rename to logging/tests/unit/handlers/transports/__init__.py diff --git a/logging/unit_tests/handlers/transports/test_background_thread.py b/logging/tests/unit/handlers/transports/test_background_thread.py similarity index 100% rename from logging/unit_tests/handlers/transports/test_background_thread.py rename to logging/tests/unit/handlers/transports/test_background_thread.py diff --git a/logging/unit_tests/handlers/transports/test_base.py b/logging/tests/unit/handlers/transports/test_base.py similarity index 100% rename from logging/unit_tests/handlers/transports/test_base.py rename to logging/tests/unit/handlers/transports/test_base.py diff --git a/logging/unit_tests/handlers/transports/test_sync.py b/logging/tests/unit/handlers/transports/test_sync.py similarity index 100% rename from logging/unit_tests/handlers/transports/test_sync.py rename to logging/tests/unit/handlers/transports/test_sync.py diff --git a/logging/unit_tests/test__gax.py b/logging/tests/unit/test__gax.py similarity index 100% rename from logging/unit_tests/test__gax.py rename to logging/tests/unit/test__gax.py diff --git a/logging/unit_tests/test__helpers.py b/logging/tests/unit/test__helpers.py similarity index 100% rename from logging/unit_tests/test__helpers.py rename to logging/tests/unit/test__helpers.py diff --git a/logging/unit_tests/test__http.py b/logging/tests/unit/test__http.py similarity index 100% rename from logging/unit_tests/test__http.py rename to logging/tests/unit/test__http.py diff --git a/logging/unit_tests/test_client.py b/logging/tests/unit/test_client.py similarity index 100% rename from logging/unit_tests/test_client.py rename to logging/tests/unit/test_client.py diff --git a/logging/unit_tests/test_entries.py b/logging/tests/unit/test_entries.py similarity index 100% rename from logging/unit_tests/test_entries.py rename to logging/tests/unit/test_entries.py diff --git a/logging/unit_tests/test_logger.py b/logging/tests/unit/test_logger.py similarity index 100% rename from logging/unit_tests/test_logger.py rename to logging/tests/unit/test_logger.py diff --git a/logging/unit_tests/test_metric.py b/logging/tests/unit/test_metric.py similarity index 100% rename from logging/unit_tests/test_metric.py rename to logging/tests/unit/test_metric.py diff --git a/logging/unit_tests/test_sink.py b/logging/tests/unit/test_sink.py similarity index 100% rename from logging/unit_tests/test_sink.py rename to logging/tests/unit/test_sink.py diff --git a/logging/tox.ini b/logging/tox.ini deleted file mode 100644 index db3e8a654954..000000000000 --- a/logging/tox.ini +++ /dev/null @@ -1,35 +0,0 @@ -[tox] -envlist = - py27,py34,py35,cover - -[testing] -localdeps = - pip install --quiet --upgrade {toxinidir}/../core -deps = - {toxinidir}/../core - mock - pytest -covercmd = - py.test --quiet \ - --cov=google.cloud.logging \ - --cov=unit_tests \ - --cov-config {toxinidir}/.coveragerc \ - unit_tests - -[testenv] -commands = - {[testing]localdeps} - py.test --quiet {posargs} unit_tests -deps = - {[testing]deps} - -[testenv:cover] -basepython = - python2.7 -commands = - {[testing]localdeps} - {[testing]covercmd} -deps = - {[testenv]deps} - coverage - pytest-cov diff --git a/monitoring/.flake8 b/monitoring/.flake8 new file mode 100644 index 000000000000..25168dc87605 --- /dev/null +++ b/monitoring/.flake8 @@ -0,0 +1,6 @@ +[flake8] +exclude = + __pycache__, + .git, + *.pyc, + conf.py diff --git a/monitoring/LICENSE b/monitoring/LICENSE new file mode 100644 index 000000000000..d64569567334 --- /dev/null +++ b/monitoring/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed 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. diff --git a/monitoring/MANIFEST.in b/monitoring/MANIFEST.in index cb3a2b9ef4fa..9f7100c9528a 100644 --- a/monitoring/MANIFEST.in +++ b/monitoring/MANIFEST.in @@ -1,4 +1,4 @@ -include README.rst -graft google -graft unit_tests -global-exclude *.pyc +include README.rst LICENSE +recursive-include google *.json *.proto +recursive-include unit_tests * +global-exclude *.pyc __pycache__ diff --git a/monitoring/nox.py b/monitoring/nox.py new file mode 100644 index 000000000000..75eba7590c78 --- /dev/null +++ b/monitoring/nox.py @@ -0,0 +1,87 @@ +# Copyright 2016 Google Inc. +# +# Licensed 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. + +from __future__ import absolute_import + +import os + +import nox + + +@nox.session +@nox.parametrize('python_version', ['2.7', '3.4', '3.5', '3.6']) +def unit_tests(session, python_version): + """Run the unit test suite.""" + + # Run unit tests against all supported versions of Python. + session.interpreter = 'python%s' % python_version + + # Install all test dependencies, then install this package in-place. + session.install('mock', 'pytest', 'pytest-cov', '../core/') + session.install('-e', '.') + + # Run py.test against the unit tests. + session.run('py.test', '--quiet', + '--cov=google.cloud.monitoring', '--cov=tests.unit', '--cov-append', + '--cov-config=.coveragerc', '--cov-report=', '--cov-fail-under=97', + 'tests/unit', + ) + + +@nox.session +@nox.parametrize('python_version', ['2.7', '3.6']) +def system_tests(session, python_version): + """Run the system test suite.""" + + # Sanity check: Only run system tests if the environment variable is set. + if not os.environ.get('GOOGLE_APPLICATION_CREDENTIALS', ''): + return + + # Run the system tests against latest Python 2 and Python 3 only. + session.interpreter = 'python%s' % python_version + + # Install all test dependencies, then install this package into the + # virutalenv's dist-packages. + session.install('mock', 'pytest', + '../core/', '../test_utils/') + session.install('.') + + # Run py.test against the system tests. + session.run('py.test', '--quiet', 'tests/system.py') + + +@nox.session +def lint(session): + """Run flake8. + + Returns a failure if flake8 finds linting errors or sufficiently + serious code quality issues. + """ + session.interpreter = 'python3.6' + session.install('flake8') + session.install('.') + session.run('flake8', 'google/cloud/monitoring') + + +@nox.session +def cover(session): + """Run the final coverage report. + + This outputs the coverage report aggregating coverage from the unit + test runs (not system test runs), and then erases coverage data. + """ + session.interpreter = 'python3.6' + session.install('coverage', 'pytest-cov') + session.run('coverage', 'report', '--show-missing', '--fail-under=100') + session.run('coverage', 'erase') diff --git a/monitoring/setup.py b/monitoring/setup.py index 5f98c9884b6e..a89408fb51b8 100644 --- a/monitoring/setup.py +++ b/monitoring/setup.py @@ -62,7 +62,7 @@ 'google', 'google.cloud', ], - packages=find_packages(), + packages=find_packages(exclude=('unit_tests*',)), install_requires=REQUIREMENTS, **SETUP_BASE ) diff --git a/monitoring/tests/__init__.py b/monitoring/tests/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/system_tests/monitoring.py b/monitoring/tests/system.py similarity index 98% rename from system_tests/monitoring.py rename to monitoring/tests/system.py index 7305ae2f61bf..46eb5a40f72c 100644 --- a/system_tests/monitoring.py +++ b/monitoring/tests/system.py @@ -20,9 +20,9 @@ from google.cloud.exceptions import ServiceUnavailable from google.cloud import monitoring -from retry import RetryErrors -from retry import RetryResult -from system_test_utils import unique_resource_id +from test_utils.retry import RetryErrors +from test_utils.retry import RetryResult +from test_utils.system import unique_resource_id retry_404 = RetryErrors(NotFound, max_tries=5) retry_404_500 = RetryErrors((NotFound, InternalServerError)) @@ -202,7 +202,7 @@ def test_write_point(self): retry_500(client.write_point)(metric, resource, VALUE) def _query_timeseries_with_retries(): - MAX_RETRIES = 7 + MAX_RETRIES = 10 def _has_timeseries(result): return len(list(result)) > 0 diff --git a/monitoring/unit_tests/__init__.py b/monitoring/tests/unit/__init__.py similarity index 100% rename from monitoring/unit_tests/__init__.py rename to monitoring/tests/unit/__init__.py diff --git a/monitoring/unit_tests/test__dataframe.py b/monitoring/tests/unit/test__dataframe.py similarity index 100% rename from monitoring/unit_tests/test__dataframe.py rename to monitoring/tests/unit/test__dataframe.py diff --git a/monitoring/unit_tests/test__http.py b/monitoring/tests/unit/test__http.py similarity index 100% rename from monitoring/unit_tests/test__http.py rename to monitoring/tests/unit/test__http.py diff --git a/monitoring/unit_tests/test_client.py b/monitoring/tests/unit/test_client.py similarity index 100% rename from monitoring/unit_tests/test_client.py rename to monitoring/tests/unit/test_client.py diff --git a/monitoring/unit_tests/test_group.py b/monitoring/tests/unit/test_group.py similarity index 100% rename from monitoring/unit_tests/test_group.py rename to monitoring/tests/unit/test_group.py diff --git a/monitoring/unit_tests/test_label.py b/monitoring/tests/unit/test_label.py similarity index 100% rename from monitoring/unit_tests/test_label.py rename to monitoring/tests/unit/test_label.py diff --git a/monitoring/unit_tests/test_metric.py b/monitoring/tests/unit/test_metric.py similarity index 100% rename from monitoring/unit_tests/test_metric.py rename to monitoring/tests/unit/test_metric.py diff --git a/monitoring/unit_tests/test_query.py b/monitoring/tests/unit/test_query.py similarity index 100% rename from monitoring/unit_tests/test_query.py rename to monitoring/tests/unit/test_query.py diff --git a/monitoring/unit_tests/test_resource.py b/monitoring/tests/unit/test_resource.py similarity index 100% rename from monitoring/unit_tests/test_resource.py rename to monitoring/tests/unit/test_resource.py diff --git a/monitoring/unit_tests/test_timeseries.py b/monitoring/tests/unit/test_timeseries.py similarity index 100% rename from monitoring/unit_tests/test_timeseries.py rename to monitoring/tests/unit/test_timeseries.py diff --git a/monitoring/tox.ini b/monitoring/tox.ini deleted file mode 100644 index f9ba521efeed..000000000000 --- a/monitoring/tox.ini +++ /dev/null @@ -1,36 +0,0 @@ -[tox] -envlist = - py27,py34,py35,cover,{py27,py34,py35}-{pandas} - -[testing] -localdeps = - pip install --quiet --upgrade {toxinidir}/../core -deps = - {toxinidir}/../core - pytest - mock -covercmd = - py.test --quiet \ - --cov=google.cloud.monitoring \ - --cov=unit_tests \ - --cov-config {toxinidir}/.coveragerc \ - unit_tests - -[testenv] -commands = - {[testing]localdeps} - py.test --quiet {posargs} unit_tests -deps = - {[testing]deps} - pandas: pandas - -[testenv:cover] -basepython = - python2.7 -commands = - {[testing]localdeps} - {[testing]covercmd} -deps = - {[testing]deps} - coverage - pytest-cov diff --git a/nox.py b/nox.py new file mode 100644 index 000000000000..5a22ff9f4f6a --- /dev/null +++ b/nox.py @@ -0,0 +1,39 @@ +# Copyright 2017 Google Inc. +# +# Licensed 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. + +import os + +import nox + + +@nox.session +def docs(session): + """Build the docs.""" + + # Build docs against the latest version of Python, because we can. + session.interpreter = 'python3.6' + + # Install Sphinx and also all of the google-cloud-* packages. + session.chdir(os.path.realpath(os.path.dirname(__file__))) + session.install('sphinx', 'sphinx_rtd_theme') + session.install( + 'core/', 'bigquery/', 'bigtable/', 'datastore/', 'error_reporting/', + 'language/', 'logging/', 'monitoring/', 'pubsub/', 'resource_manager/', + 'runtimeconfig/', 'spanner/', 'speech/', 'storage/', 'translate/', + 'vision/', + ) + session.install('.') + + # Build the docs! + session.run('bash', './test_utils/scripts/update_docs.sh') diff --git a/pubsub/.flake8 b/pubsub/.flake8 new file mode 100644 index 000000000000..25168dc87605 --- /dev/null +++ b/pubsub/.flake8 @@ -0,0 +1,6 @@ +[flake8] +exclude = + __pycache__, + .git, + *.pyc, + conf.py diff --git a/pubsub/LICENSE b/pubsub/LICENSE new file mode 100644 index 000000000000..d64569567334 --- /dev/null +++ b/pubsub/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed 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. diff --git a/pubsub/MANIFEST.in b/pubsub/MANIFEST.in index cb3a2b9ef4fa..9f7100c9528a 100644 --- a/pubsub/MANIFEST.in +++ b/pubsub/MANIFEST.in @@ -1,4 +1,4 @@ -include README.rst -graft google -graft unit_tests -global-exclude *.pyc +include README.rst LICENSE +recursive-include google *.json *.proto +recursive-include unit_tests * +global-exclude *.pyc __pycache__ diff --git a/pubsub/google/cloud/pubsub/__init__.py b/pubsub/google/cloud/pubsub/__init__.py index 6e6f29644727..070e8243bf2b 100644 --- a/pubsub/google/cloud/pubsub/__init__.py +++ b/pubsub/google/cloud/pubsub/__init__.py @@ -30,3 +30,5 @@ from google.cloud.pubsub.client import Client from google.cloud.pubsub.subscription import Subscription from google.cloud.pubsub.topic import Topic + +__all__ = ['__version__', 'Client', 'Subscription', 'Topic'] diff --git a/pubsub/nox.py b/pubsub/nox.py new file mode 100644 index 000000000000..1f0ae6130bf7 --- /dev/null +++ b/pubsub/nox.py @@ -0,0 +1,87 @@ +# Copyright 2016 Google Inc. +# +# Licensed 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. + +from __future__ import absolute_import + +import os + +import nox + + +@nox.session +@nox.parametrize('python_version', ['2.7', '3.4', '3.5', '3.6']) +def unit_tests(session, python_version): + """Run the unit test suite.""" + + # Run unit tests against all supported versions of Python. + session.interpreter = 'python%s' % python_version + + # Install all test dependencies, then install this package in-place. + session.install('mock', 'pytest', 'pytest-cov', '../core/') + session.install('-e', '.') + + # Run py.test against the unit tests. + session.run('py.test', '--quiet', + '--cov=google.cloud.pubsub', '--cov=tests.unit', '--cov-append', + '--cov-config=.coveragerc', '--cov-report=', '--cov-fail-under=97', + 'tests/unit', + ) + + +@nox.session +@nox.parametrize('python_version', ['2.7', '3.6']) +def system_tests(session, python_version): + """Run the system test suite.""" + + # Sanity check: Only run system tests if the environment variable is set. + if not os.environ.get('GOOGLE_APPLICATION_CREDENTIALS', ''): + return + + # Run the system tests against latest Python 2 and Python 3 only. + session.interpreter = 'python%s' % python_version + + # Install all test dependencies, then install this package into the + # virutalenv's dist-packages. + session.install('mock', 'pytest', + '../core/', '../test_utils/') + session.install('.') + + # Run py.test against the system tests. + session.run('py.test', '--quiet', 'tests/system.py') + + +@nox.session +def lint(session): + """Run flake8. + + Returns a failure if flake8 finds linting errors or sufficiently + serious code quality issues. + """ + session.interpreter = 'python3.6' + session.install('flake8') + session.install('.') + session.run('flake8', 'google/cloud/pubsub') + + +@nox.session +def cover(session): + """Run the final coverage report. + + This outputs the coverage report aggregating coverage from the unit + test runs (not system test runs), and then erases coverage data. + """ + session.interpreter = 'python3.6' + session.install('coverage', 'pytest-cov') + session.run('coverage', 'report', '--show-missing', '--fail-under=100') + session.run('coverage', 'erase') diff --git a/pubsub/setup.py b/pubsub/setup.py index 9f74c1e49e71..655385112b7b 100644 --- a/pubsub/setup.py +++ b/pubsub/setup.py @@ -64,7 +64,7 @@ 'google', 'google.cloud', ], - packages=find_packages(), + packages=find_packages(exclude=('unit_tests*',)), install_requires=REQUIREMENTS, **SETUP_BASE ) diff --git a/pubsub/tests/__init__.py b/pubsub/tests/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/system_tests/pubsub.py b/pubsub/tests/system.py similarity index 97% rename from system_tests/pubsub.py rename to pubsub/tests/system.py index 646374f635cf..c1fad735bbf9 100644 --- a/system_tests/pubsub.py +++ b/pubsub/tests/system.py @@ -23,11 +23,11 @@ from google.cloud.environment_vars import PUBSUB_EMULATOR from google.cloud.pubsub import client -from retry import RetryInstanceState -from retry import RetryResult -from retry import RetryErrors -from system_test_utils import EmulatorCreds -from system_test_utils import unique_resource_id +from test_utils.retry import RetryInstanceState +from test_utils.retry import RetryResult +from test_utils.retry import RetryErrors +from test_utils.system import EmulatorCreds +from test_utils.system import unique_resource_id def _unavailable(exc): diff --git a/pubsub/unit_tests/__init__.py b/pubsub/tests/unit/__init__.py similarity index 100% rename from pubsub/unit_tests/__init__.py rename to pubsub/tests/unit/__init__.py diff --git a/pubsub/unit_tests/test__gax.py b/pubsub/tests/unit/test__gax.py similarity index 100% rename from pubsub/unit_tests/test__gax.py rename to pubsub/tests/unit/test__gax.py diff --git a/pubsub/unit_tests/test__helpers.py b/pubsub/tests/unit/test__helpers.py similarity index 100% rename from pubsub/unit_tests/test__helpers.py rename to pubsub/tests/unit/test__helpers.py diff --git a/pubsub/unit_tests/test__http.py b/pubsub/tests/unit/test__http.py similarity index 100% rename from pubsub/unit_tests/test__http.py rename to pubsub/tests/unit/test__http.py diff --git a/pubsub/unit_tests/test_client.py b/pubsub/tests/unit/test_client.py similarity index 100% rename from pubsub/unit_tests/test_client.py rename to pubsub/tests/unit/test_client.py diff --git a/pubsub/unit_tests/test_iam.py b/pubsub/tests/unit/test_iam.py similarity index 100% rename from pubsub/unit_tests/test_iam.py rename to pubsub/tests/unit/test_iam.py diff --git a/pubsub/unit_tests/test_message.py b/pubsub/tests/unit/test_message.py similarity index 100% rename from pubsub/unit_tests/test_message.py rename to pubsub/tests/unit/test_message.py diff --git a/pubsub/unit_tests/test_subscription.py b/pubsub/tests/unit/test_subscription.py similarity index 100% rename from pubsub/unit_tests/test_subscription.py rename to pubsub/tests/unit/test_subscription.py diff --git a/pubsub/unit_tests/test_topic.py b/pubsub/tests/unit/test_topic.py similarity index 100% rename from pubsub/unit_tests/test_topic.py rename to pubsub/tests/unit/test_topic.py diff --git a/pubsub/tox.ini b/pubsub/tox.ini deleted file mode 100644 index 001886c51e9a..000000000000 --- a/pubsub/tox.ini +++ /dev/null @@ -1,35 +0,0 @@ -[tox] -envlist = - py27,py34,py35,cover - -[testing] -localdeps = - pip install --quiet --upgrade {toxinidir}/../core -deps = - {toxinidir}/../core - mock - pytest -covercmd = - py.test --quiet \ - --cov=google.cloud.pubsub \ - --cov=unit_tests \ - --cov-config {toxinidir}/.coveragerc \ - unit_tests - -[testenv] -commands = - {[testing]localdeps} - py.test --quiet {posargs} unit_tests -deps = - {[testing]deps} - -[testenv:cover] -basepython = - python2.7 -commands = - {[testing]localdeps} - {[testing]covercmd} -deps = - {[testenv]deps} - coverage - pytest-cov diff --git a/resource_manager/.flake8 b/resource_manager/.flake8 new file mode 100644 index 000000000000..25168dc87605 --- /dev/null +++ b/resource_manager/.flake8 @@ -0,0 +1,6 @@ +[flake8] +exclude = + __pycache__, + .git, + *.pyc, + conf.py diff --git a/resource_manager/LICENSE b/resource_manager/LICENSE new file mode 100644 index 000000000000..d64569567334 --- /dev/null +++ b/resource_manager/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed 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. diff --git a/resource_manager/MANIFEST.in b/resource_manager/MANIFEST.in index cb3a2b9ef4fa..9f7100c9528a 100644 --- a/resource_manager/MANIFEST.in +++ b/resource_manager/MANIFEST.in @@ -1,4 +1,4 @@ -include README.rst -graft google -graft unit_tests -global-exclude *.pyc +include README.rst LICENSE +recursive-include google *.json *.proto +recursive-include unit_tests * +global-exclude *.pyc __pycache__ diff --git a/resource_manager/google/cloud/resource_manager/__init__.py b/resource_manager/google/cloud/resource_manager/__init__.py index e69c0ba25cb2..42f0e5884c9d 100644 --- a/resource_manager/google/cloud/resource_manager/__init__.py +++ b/resource_manager/google/cloud/resource_manager/__init__.py @@ -23,3 +23,5 @@ SCOPE = Client.SCOPE + +__all__ = ['__version__', 'Client', 'Project', 'SCOPE'] diff --git a/resource_manager/nox.py b/resource_manager/nox.py new file mode 100644 index 000000000000..0f30e57cebfd --- /dev/null +++ b/resource_manager/nox.py @@ -0,0 +1,65 @@ +# Copyright 2016 Google Inc. +# +# Licensed 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. + +from __future__ import absolute_import + +import os + +import nox + + +@nox.session +@nox.parametrize('python_version', ['2.7', '3.4', '3.5', '3.6']) +def unit_tests(session, python_version): + """Run the unit test suite.""" + + # Run unit tests against all supported versions of Python. + session.interpreter = 'python%s' % python_version + + # Install all test dependencies, then install this package in-place. + session.install('mock', 'pytest', 'pytest-cov', '../core/') + session.install('-e', '.') + + # Run py.test against the unit tests. + session.run('py.test', '--quiet', + '--cov=google.cloud.resource_manager', '--cov=tests.unit', '--cov-append', + '--cov-config=.coveragerc', '--cov-report=', '--cov-fail-under=97', + 'tests/unit', + ) + + +@nox.session +def lint(session): + """Run flake8. + + Returns a failure if flake8 finds linting errors or sufficiently + serious code quality issues. + """ + session.interpreter = 'python3.6' + session.install('flake8') + session.install('.') + session.run('flake8', 'google/cloud/resource_manager') + + +@nox.session +def cover(session): + """Run the final coverage report. + + This outputs the coverage report aggregating coverage from the unit + test runs (not system test runs), and then erases coverage data. + """ + session.interpreter = 'python3.6' + session.install('coverage', 'pytest-cov') + session.run('coverage', 'report', '--show-missing', '--fail-under=100') + session.run('coverage', 'erase') diff --git a/resource_manager/setup.py b/resource_manager/setup.py index b8b6813fe393..171f5377dbd2 100644 --- a/resource_manager/setup.py +++ b/resource_manager/setup.py @@ -62,7 +62,7 @@ 'google', 'google.cloud', ], - packages=find_packages(), + packages=find_packages(exclude=('unit_tests*',)), install_requires=REQUIREMENTS, **SETUP_BASE ) diff --git a/resource_manager/tests/__init__.py b/resource_manager/tests/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/resource_manager/unit_tests/__init__.py b/resource_manager/tests/unit/__init__.py similarity index 100% rename from resource_manager/unit_tests/__init__.py rename to resource_manager/tests/unit/__init__.py diff --git a/resource_manager/unit_tests/test__http.py b/resource_manager/tests/unit/test__http.py similarity index 100% rename from resource_manager/unit_tests/test__http.py rename to resource_manager/tests/unit/test__http.py diff --git a/resource_manager/unit_tests/test_client.py b/resource_manager/tests/unit/test_client.py similarity index 100% rename from resource_manager/unit_tests/test_client.py rename to resource_manager/tests/unit/test_client.py diff --git a/resource_manager/unit_tests/test_project.py b/resource_manager/tests/unit/test_project.py similarity index 100% rename from resource_manager/unit_tests/test_project.py rename to resource_manager/tests/unit/test_project.py diff --git a/resource_manager/tox.ini b/resource_manager/tox.ini deleted file mode 100644 index 9e1f41a089d3..000000000000 --- a/resource_manager/tox.ini +++ /dev/null @@ -1,35 +0,0 @@ -[tox] -envlist = - py27,py34,py35,cover - -[testing] -localdeps = - pip install --quiet --upgrade {toxinidir}/../core -deps = - {toxinidir}/../core - mock - pytest -covercmd = - py.test --quiet \ - --cov=google.cloud.resource-manager \ - --cov=unit_tests \ - --cov-config {toxinidir}/.coveragerc \ - unit_tests - -[testenv] -commands = - {[testing]localdeps} - py.test --quiet {posargs} unit_tests -deps = - {[testing]deps} - -[testenv:cover] -basepython = - python2.7 -commands = - {[testing]localdeps} - {[testing]covercmd} -deps = - {[testenv]deps} - coverage - pytest-cov diff --git a/runtimeconfig/.flake8 b/runtimeconfig/.flake8 new file mode 100644 index 000000000000..25168dc87605 --- /dev/null +++ b/runtimeconfig/.flake8 @@ -0,0 +1,6 @@ +[flake8] +exclude = + __pycache__, + .git, + *.pyc, + conf.py diff --git a/runtimeconfig/LICENSE b/runtimeconfig/LICENSE new file mode 100644 index 000000000000..d64569567334 --- /dev/null +++ b/runtimeconfig/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed 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. diff --git a/runtimeconfig/MANIFEST.in b/runtimeconfig/MANIFEST.in index cb3a2b9ef4fa..9f7100c9528a 100644 --- a/runtimeconfig/MANIFEST.in +++ b/runtimeconfig/MANIFEST.in @@ -1,4 +1,4 @@ -include README.rst -graft google -graft unit_tests -global-exclude *.pyc +include README.rst LICENSE +recursive-include google *.json *.proto +recursive-include unit_tests * +global-exclude *.pyc __pycache__ diff --git a/runtimeconfig/google/cloud/runtimeconfig/__init__.py b/runtimeconfig/google/cloud/runtimeconfig/__init__.py index 73c8d2bc8283..8acc57f4c86a 100644 --- a/runtimeconfig/google/cloud/runtimeconfig/__init__.py +++ b/runtimeconfig/google/cloud/runtimeconfig/__init__.py @@ -19,3 +19,5 @@ __version__ = get_distribution('google-cloud-runtimeconfig').version from google.cloud.runtimeconfig.client import Client + +__all__ = ['__version__', 'Client'] diff --git a/runtimeconfig/nox.py b/runtimeconfig/nox.py new file mode 100644 index 000000000000..bf8603e1e72a --- /dev/null +++ b/runtimeconfig/nox.py @@ -0,0 +1,65 @@ +# Copyright 2016 Google Inc. +# +# Licensed 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. + +from __future__ import absolute_import + +import os + +import nox + + +@nox.session +@nox.parametrize('python_version', ['2.7', '3.4', '3.5', '3.6']) +def unit_tests(session, python_version): + """Run the unit test suite.""" + + # Run unit tests against all supported versions of Python. + session.interpreter = 'python%s' % python_version + + # Install all test dependencies, then install this package in-place. + session.install('mock', 'pytest', 'pytest-cov', '../core/') + session.install('-e', '.') + + # Run py.test against the unit tests. + session.run('py.test', '--quiet', + '--cov=google.cloud.runtimeconfig', '--cov=tests.unit', '--cov-append', + '--cov-config=.coveragerc', '--cov-report=', '--cov-fail-under=97', + 'tests/unit', + ) + + +@nox.session +def lint(session): + """Run flake8. + + Returns a failure if flake8 finds linting errors or sufficiently + serious code quality issues. + """ + session.interpreter = 'python3.6' + session.install('flake8') + session.install('.') + session.run('flake8', 'google/cloud/runtimeconfig') + + +@nox.session +def cover(session): + """Run the final coverage report. + + This outputs the coverage report aggregating coverage from the unit + test runs (not system test runs), and then erases coverage data. + """ + session.interpreter = 'python3.6' + session.install('coverage', 'pytest-cov') + session.run('coverage', 'report', '--show-missing', '--fail-under=100') + session.run('coverage', 'erase') diff --git a/runtimeconfig/setup.py b/runtimeconfig/setup.py index 10a90b736bb0..09c228b91597 100644 --- a/runtimeconfig/setup.py +++ b/runtimeconfig/setup.py @@ -62,7 +62,7 @@ 'google', 'google.cloud', ], - packages=find_packages(), + packages=find_packages(exclude=('unit_tests*',)), install_requires=REQUIREMENTS, **SETUP_BASE ) diff --git a/runtimeconfig/unit_tests/__init__.py b/runtimeconfig/tests/unit/__init__.py similarity index 100% rename from runtimeconfig/unit_tests/__init__.py rename to runtimeconfig/tests/unit/__init__.py diff --git a/runtimeconfig/unit_tests/test__helpers.py b/runtimeconfig/tests/unit/test__helpers.py similarity index 100% rename from runtimeconfig/unit_tests/test__helpers.py rename to runtimeconfig/tests/unit/test__helpers.py diff --git a/runtimeconfig/unit_tests/test__http.py b/runtimeconfig/tests/unit/test__http.py similarity index 100% rename from runtimeconfig/unit_tests/test__http.py rename to runtimeconfig/tests/unit/test__http.py diff --git a/runtimeconfig/unit_tests/test_client.py b/runtimeconfig/tests/unit/test_client.py similarity index 100% rename from runtimeconfig/unit_tests/test_client.py rename to runtimeconfig/tests/unit/test_client.py diff --git a/runtimeconfig/unit_tests/test_config.py b/runtimeconfig/tests/unit/test_config.py similarity index 100% rename from runtimeconfig/unit_tests/test_config.py rename to runtimeconfig/tests/unit/test_config.py diff --git a/runtimeconfig/unit_tests/test_variable.py b/runtimeconfig/tests/unit/test_variable.py similarity index 100% rename from runtimeconfig/unit_tests/test_variable.py rename to runtimeconfig/tests/unit/test_variable.py diff --git a/runtimeconfig/tox.ini b/runtimeconfig/tox.ini deleted file mode 100644 index 03a0428a1ee0..000000000000 --- a/runtimeconfig/tox.ini +++ /dev/null @@ -1,35 +0,0 @@ -[tox] -envlist = - py27,py34,py35,cover - -[testing] -localdeps = - pip install --quiet --upgrade {toxinidir}/../core -deps = - {toxinidir}/../core - mock - pytest -covercmd = - py.test --quiet \ - --cov=google.cloud.runtimeconfig \ - --cov=unit_tests \ - --cov-config {toxinidir}/.coveragerc \ - unit_tests - -[testenv] -commands = - {[testing]localdeps} - py.test --quiet {posargs} unit_tests -deps = - {[testing]deps} - -[testenv:cover] -basepython = - python2.7 -commands = - {[testing]localdeps} - {[testing]covercmd} -deps = - {[testenv]deps} - coverage - pytest-cov diff --git a/scripts/coveralls.sh b/scripts/coveralls.sh deleted file mode 100755 index e88107087224..000000000000 --- a/scripts/coveralls.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/bin/bash - -# Copyright 2016 Google Inc. -# -# Licensed 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. - -set -ev - -############################################################# -# Only run coveralls during a CircleCI / Travis push build. # -############################################################# -if [[ "${TRAVIS_BRANCH}" == "master" ]] && \ - [[ "${TRAVIS_PULL_REQUEST}" == "false" ]]; then - echo "Sending coverage report on a Travis merge to master." -elif [[ "${CIRCLECI}" == "true" ]] && \ - [[ "${CIRCLE_BRANCH}" == "master" ]] && \ - [[ "${CI_PULL_REQUEST}" == "" ]]; then - echo "Sending coverage report on a CircleCI merge to master." -else - echo "Not on a push build for master, skipping coveralls." - exit -fi - -tox -e coveralls diff --git a/scripts/generate_json_docs.py b/scripts/generate_json_docs.py deleted file mode 100644 index cda8a374ebe2..000000000000 --- a/scripts/generate_json_docs.py +++ /dev/null @@ -1,658 +0,0 @@ -# Copyright 2016 Google Inc. -# -# Licensed 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. - -import argparse -import cgi -import doctest -import inspect -import json -import os -import shutil -import types - -import pdoc -from parinx.parser import parse_docstring -from parinx.errors import MethodParsingException -import six - -from script_utils import PROJECT_ROOT -from verify_included_modules import get_public_modules - - -_DOCSTRING_TEST_PARSER = doctest.DocTestParser() - - -class Module(object): - - def __init__(self, module_id, name, description=None, - examples=None, methods=None, source=None): - self.module_id = module_id - self.name = name - self.description = description - self.examples = examples or [] - self.methods = methods - self.source = source - - @classmethod - def from_module_name(cls, name, base_path): - module = pdoc.Module(pdoc.import_module(name), allsubmodules=True) - methods = module.functions() + module.variables() - examples = [] - - if '__init__' in name: - snippets = get_snippet_examples(name.split('.')[1], - os.path.join(base_path, 'docs')) - examples.extend(snippets) - - source_path = clean_source_path(module) - - return cls(module_id=name, - name=name.split('.')[-1].title(), - description=module.docstring, - examples=examples or [], - methods=[Method.from_pdoc(m) for m in methods], - source=source_path) - - def to_dict(self): - return {'id': self.module_id, - 'name': self.name, - 'description': format_sphinx_doc(self.description), - 'examples': self.examples, - 'methods': [m.to_dict() for m in self.methods], - 'source': self.source} - - -class Klass(object): - def __init__(self, module_id, name, refname=None, description=None, - examples=None, methods=None, source=None): - self.module_id = module_id - self.name = name - self.refname = refname - self.description = description - self.examples = examples or [] - self.methods = methods - self.source = source - - @classmethod - def from_class_name(cls, module, kls): - methods = kls.methods() - - examples = [] - source_path = clean_source_path(module) - - return cls(module_id=kls.name, - name=kls.name.split('.')[-1].title(), - refname=module.refname, - description=module.docstring, - examples=examples, - methods=[Method.from_pdoc(m) for m in methods], - source=source_path) - - def to_dict(self): - return {'id': '%s.%s' % (self.refname, self.name.lower()), - 'name': self.name, - 'description': format_sphinx_doc(self.description), - 'examples': self.examples, - 'methods': [m.to_dict() for m in self.methods], - 'source': self.source} - - -class Method(object): - - def __init__(self, method_id, name, is_class, examples=None, params=None, - exceptions=None, returns=None, source=None): - self.method_id = method_id - self.name = name - self.examples = examples or [] - self.params = params or [] - self.exceptions = exceptions or [] - self.returns = returns or [] - self.source = source or '' - - if is_class: - self.type = 'constructor' - else: - self.type = 'instance' - - def add_param(self, param): - self.params.append(param) - - def add_example(self, example): - self.examples.append({'code': example}) - - def add_source_line(self, source_line): - self.source = source_line - - def set_returns(self, return_types): - self.returns = [{'types': [return_types]}] - - def to_dict(self): - return {'id': self.method_id, - 'name': self.name, - 'examples': self.examples, - 'source': self.source, - 'params': [p.to_dict() for p in self.params], - 'exceptions': self.exceptions, - 'returns': self.returns, - 'type': self.type} - - @classmethod - def from_pdoc(cls, element): - is_class = isinstance(element, pdoc.Class) - method = cls(element.refname, element.name, is_class) - components = element.refname.split('.') - - mod = __import__(components[0]) - for comp in components[1:]: - mod = getattr(mod, comp) - - # Get method line number. - method.add_source_line(get_source_line_number(mod)) - - # Get method Examples. - examples = get_examples_from_docstring(element.docstring) - if examples: - method.add_example(examples) - - if element.docstring: - if not isinstance(element, pdoc.Class) and element.cls: - klass = element.cls.cls - elif element.cls: - klass = element.cls - else: - klass = None - - # Hack for old-style classes - if not str(klass).startswith('<'): - klass = '' % (klass,) - - try: - method_info = parse_docstring(element.docstring, klass) - except (MethodParsingException, IndexError): - return method - - for name, data in method_info['arguments'].items(): - param = Param.from_docstring_section(name, data) - method.add_param(param) - - if method_info.get('return'): - if len(method_info['return']['type_name']) > 0: - type_name = method_info.get('return').get('type_name') - - type_type = 'instance' - if any(x.isupper() for x in type_name): - type_type = 'constructor' - - type_markup = build_link_from_type(type_name, type_type) - - method.set_returns(type_markup) - - return method - - -class Param(object): - - def __init__(self, name, description=None, param_types=None, optional=None, - nullable=None): - self.name = name - self.description = description - self.param_types = param_types or [] - self.optional = optional - self.nullable = nullable - - def to_dict(self): - return {'name': self.name, - 'description': process_words(self.description), - 'types': build_link_from_list_of_types(self.param_types), - 'optional': self.optional, - 'nullable': self.nullable} - - @classmethod - def from_docstring_section(cls, name, data): - param_types = build_link_from_type(data['type_name']) - return cls(name=name, description=data['description'], - param_types=[param_types]) - - -def clean_type_name(type_name): - if type_name.lower().startswith('list of'): - type_name = (type_name.replace('list of', '') - .replace('List of', '')) - type_name = (type_name.replace('`', '').replace('class', '') - .replace(':', '')) - return type_name - - -def build_link_from_list_of_types(type_names, object_type=None): - processed_types = [] - for type_link in type_names: - type_link = clean_type_name(type_link) - processed_types.append(build_link_from_type(type_link, object_type)) - return processed_types - - -def build_link_from_type(type_name, object_type=None): - type_name = clean_type_name(type_name) - - if not type_name.startswith('google.cloud'): - return type_name - doc_path = type_name - - doc_path = '/'.join(doc_path.split('.')).lower() - - type_markup = '%s' % (block,)} - - formatted_blocks.append(block) - return formatted_blocks - - -def format_sphinx_doc(doc): - doc = process_code_blocks(doc) - example_lines = "" - for line in doc: - if isinstance(line, dict): - line = line['code'] - else: - line = process_words(line) - example_lines = '%s\n%s' % (example_lines, line) - return example_lines - - -def process_words(line): - processed_line = '' - for word in line.split(): - end_sentence = False - if word.endswith('.'): - end_sentence = True - word = word[:-1] - - if word.startswith('``') and word.endswith('``'): - word = word.replace('``', '') - word = '%s' % (word,) - - if word.startswith('**') and word.endswith('**'): - word = word.replace('**', '') - word = '%s' % (word,) - - if word.startswith(':class:'): - word = word.replace(':class:', '').replace('`', '') - word = build_link_from_type(word) - - if word.startswith(':mod:'): - word = word.replace(':mod:', '').replace('`', '') - word = build_link_from_type(word) - - if word.startswith(':meth:'): - word = word.replace(':meth:', '').replace('`', '') - word = build_link_from_type(word) - - if word.startswith(':func:'): - word = word.replace(':func:', '').replace('`', '') - word = build_link_from_type(word) - - word = word.replace('`', '') - - if word.startswith('https://') or word.startswith('http://'): - word = '%s' % (word, word) - - if end_sentence: - word += '.' - - processed_line += ' %s' % (word,) - - processed_line = processed_line.replace('::', '') - - return processed_line - - -def write_docs_file(path, contents): - if not os.path.exists(os.path.dirname(path)): - try: - os.makedirs(os.path.dirname(path)) - except OSError: - raise - with open(path, 'w') as output_file: - output_file.write(contents) - - -def generate_doc_types_json(modules, types_file_path): - doc_types_list = [{ - 'id': 'google-cloud', - 'contents': 'index.json', - 'title': 'google-cloud' - }] - - for module_name in modules: - if module_name == 'google.cloud.__init__': - continue - - module_title = module_name.replace('.__init__', '').split('.') - module_contents = (module_name.replace('.', '/') - .replace('__init__', 'index')) - - if len(module_name.split('.')) > 2: - module_id = module_name.replace('.', '/') - else: - module_id = (module_name.replace('.', '/')) - - module_contents += '.json' - - doc_type_object = build_type(module_id.replace('/__init__', ''), - module_title, module_contents) - - doc_types_list.append(doc_type_object) - - pdoc_module = pdoc.Module(pdoc.import_module(module_name), - allsubmodules=True) - for c in pdoc_module.classes(): - generate_doc_types_classes_json(c, doc_types_list) - - write_docs_file(types_file_path, - json.dumps(doc_types_list)) - - -def generate_doc_types_classes_json(klass, doc_types_list): - - module_contents = (klass.refname.replace('.', '/') - .replace('__init__', 'index')) - module_contents += '.json' - - doc_type_object = build_type(klass.refname.lower().replace('.', '/'), - klass.refname.split('.'), - module_contents.lower()) - - doc_types_list.append(doc_type_object) - - -def generate_module_docs(modules, docs_path, real_base_path, toc): - for module_name in modules: - module = Module.from_module_name(module_name, real_base_path) - pdoc_module = pdoc.Module(pdoc.import_module(module_name), - allsubmodules=True) - for c in pdoc_module.classes(): - generate_class_docs(pdoc_module, c, docs_path, toc) - - module_path = (module_name.replace('.', '/') - .replace('__init__', 'index')) - module_docs_path = os.path.join(docs_path, module_path) + '.json' - - if pdoc_module.functions(): - toc_key = module_name.replace('google.cloud.', '').split('.')[0] - toc_entry = build_toc_entry(module.name, module_path) - toc['services'][toc_key].append(toc_entry) - - write_docs_file(module_docs_path, - json.dumps(module.to_dict(), - indent=2, sort_keys=True)) - - -def generate_class_docs(module, klass, base_path, toc): - kls = Klass.from_class_name(module, klass) - - module_path = (module.refname.replace('.', '/') - .replace('__init__', 'index')) - module_docs_path = os.path.join(base_path, module_path, - klass.name.lower()) + '.json' - toc_key = module.name.replace('google.cloud.', '').split('.')[0] - - toc_entry = build_toc_entry(klass.name, - os.path.join(module_path, - klass.name.lower())) - - toc['services'][toc_key].append(toc_entry) - - write_docs_file(module_docs_path, - json.dumps(kls.to_dict(), - indent=2, sort_keys=True)) - - -def get_snippet_examples(module, json_docs_dir): - snippets_file_path = os.path.join(json_docs_dir, - module + '_snippets.py') - usage_rst_path = os.path.join(json_docs_dir, module + '-usage.rst') - - snippet_labels = {} - - if os.path.isfile(usage_rst_path): - with open(usage_rst_path, 'r') as snippet_labels_file: - usage_rst = snippet_labels_file.read().splitlines() - line_index = 0 - - include_string = '.. literalinclude:: %s_snippets.py' - for line in usage_rst: - if line.startswith(include_string % module): - label_key = (usage_rst[line_index + 1] - .replace(' :start-after: [START ', '') - .replace(']', '')) - snippet_labels[label_key] = usage_rst[line_index - 2] - line_index += 1 - - snippets = [] - - if os.path.isfile(snippets_file_path): - with open(snippets_file_path, 'r') as snippets_file: - snippets_string = snippets_file.read() - label = None - snippet = '' - for line in snippets_string.splitlines(True): - if line.strip().startswith('# [END'): - example = { - 'caption': snippet_labels.get(label), - 'code': snippet - } - snippets.append(example) - - # Reset for next snippet - snippet = '' - label = None - - if label: - snippet += line - - if line.strip().startswith('# [START'): - label = (line.replace('# [START', '').replace(']', '') - .strip()) - snippet = '# %s\n' % label - return snippets - - -def package_files(generated_json_dir, docs_build_dir, static_json_dir, - tag='master'): - """Copy app and JSON files into a convenient place to deploy from. - - Structure needs to be... - root - - src/ - - images/ - - app.js - - app.css - - vendor.js - - vendor.css - - json/ - - master/ - - toc.json - - types.json - - index.json - - overview.html - - home.html - - index.html - - manifest.json - """ - package_path = os.path.join(docs_build_dir, 'json_build') - shutil.rmtree(package_path, ignore_errors=True) - - shutil.copytree(static_json_dir, package_path) - shutil.copytree(os.path.join(generated_json_dir, 'google', 'cloud'), - os.path.join(package_path, 'json', tag, 'google', 'cloud')) - shutil.copyfile(os.path.join(generated_json_dir, 'types.json'), - os.path.join(package_path, 'json', tag, 'types.json')) - - -def main(): - parser = argparse.ArgumentParser(description='Document Python modules.') - parser.add_argument('--tag', help='The version of the documentation.', - default='master') - parser.add_argument('--basepath', help='Path to the library.', - default=PROJECT_ROOT) - parser.add_argument('--show-toc', help='Prints partial table of contents', - default=False) - args = parser.parse_args() - - toc = { - 'services': { - '__init__': [], - 'google': [], - 'cloud': [], - 'bigquery': [], - 'bigtable': [], - 'client': [], - 'connection': [], - 'credentials': [], - 'datastore': [], - 'dns': [], - 'environment_vars': [], - 'error_reporting': [], - 'exceptions': [], - 'iterator': [], - 'language': [], - 'logging': [], - 'monitoring': [], - 'operation': [], - 'pubsub': [], - 'resource_manager': [], - 'storage': [], - 'streaming': [], - 'translate': [], - 'vision': [], - } - } - - BASE_JSON_DOCS_DIR = os.path.join(PROJECT_ROOT, 'docs', 'json') - - DOCS_BUILD_DIR = os.path.join(PROJECT_ROOT, 'docs', '_build') - JSON_DOCS_DIR = os.path.join(DOCS_BUILD_DIR, 'json', args.tag) - LIB_DIR = os.path.abspath(args.basepath) - - library_dir = os.path.join(LIB_DIR, 'google', 'cloud') - public_mods = get_public_modules(library_dir, - base_package='google.cloud') - - generate_module_docs(public_mods, JSON_DOCS_DIR, PROJECT_ROOT, toc) - generate_doc_types_json(public_mods, - os.path.join(JSON_DOCS_DIR, 'types.json')) - package_files(JSON_DOCS_DIR, DOCS_BUILD_DIR, BASE_JSON_DOCS_DIR) - if args.show_toc: - print(json.dumps(toc, indent=4)) - - -if __name__ == '__main__': - main() diff --git a/scripts/pycodestyle_on_repo.py b/scripts/pycodestyle_on_repo.py deleted file mode 100644 index b362dae0c6f9..000000000000 --- a/scripts/pycodestyle_on_repo.py +++ /dev/null @@ -1,49 +0,0 @@ -# Copyright 2016 Google Inc. -# -# Licensed 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. - -"""Custom script to run pycodestyle on google-cloud codebase. - -This runs pycodestyle as a script via subprocess but only runs it on the -.py files that are checked in to the repository. -""" - - -from __future__ import print_function - -import os -import subprocess -import sys - -from script_utils import get_affected_files - - -def main(): - """Run pycodestyle on all Python files in the repository.""" - git_root = subprocess.check_output( - ['git', 'rev-parse', '--show-toplevel']).strip() - os.chdir(git_root) - candidates, _ = get_affected_files() - python_files = [ - candidate for candidate in candidates if candidate.endswith('.py')] - - if not python_files: - print('No Python files to lint, exiting.') - else: - pycodestyle_command = ['pycodestyle'] + python_files - status_code = subprocess.call(pycodestyle_command) - sys.exit(status_code) - - -if __name__ == '__main__': - main() diff --git a/scripts/pylintrc_default b/scripts/pylintrc_default deleted file mode 100644 index b73902410584..000000000000 --- a/scripts/pylintrc_default +++ /dev/null @@ -1,552 +0,0 @@ -# PyLint config for 'google-cloud' *library* code. -# -# NOTES: -# -# - Rules for test code are generated into 'pylintrc_reduced' -# as deltas from this configuration by the 'run_pylint.py' script. -# -# - The majority of the content in this configuration is the -# default provided by "generate-rcfile" (from PyLint version 1.6.4). Any -# value that differs from the provided defaults will both specify -# the "DEFAULT" being replaced and a "RATIONALE" for the value -# we replace it with. -# -[MASTER] - -# Specify a configuration file. -#rcfile= - -# Python code to execute, usually for sys.path manipulation such as -# pygtk.require(). -#init-hook= - -# Add files or directories to the blacklist. They should be base names, not -# paths. -# DEFAULT: ignore=CVS -# RATIONALE: No files to ignore. -ignore= - -# Add files or directories matching the regex patterns to the blacklist. The -# regex matches against base names, not paths. -ignore-patterns= - -# Pickle collected data for later comparisons. -persistent=yes - -# List of plugins (as comma separated values of python modules names) to load, -# usually to register additional checkers. -# DEFAULT: load-plugins= -# RATIONALE: We want to make sure our docstrings match the objects -# they document. -load-plugins=pylint.extensions.check_docs - -# Use multiple processes to speed up Pylint. -jobs=1 - -# Allow loading of arbitrary C extensions. Extensions are imported into the -# active Python interpreter and may run arbitrary code. -unsafe-load-any-extension=no - -# A comma-separated list of package or module names from where C extensions may -# be loaded. Extensions are loading into the active Python interpreter and may -# run arbitrary code -extension-pkg-whitelist= - -# Allow optimization of some AST trees. This will activate a peephole AST -# optimizer, which will apply various small optimizations. For instance, it can -# be used to obtain the result of joining multiple strings with the addition -# operator. Joining a lot of strings can lead to a maximum recursion error in -# Pylint and this flag can prevent that. It has one side effect, the resulting -# AST will be different than the one from reality. This option is deprecated -# and it will be removed in Pylint 2.0. -optimize-ast=no - - -[MESSAGES CONTROL] - -# Only show warnings with the listed confidence levels. Leave empty to show -# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED -confidence= - -# Enable the message, report, category or checker with the given id(s). You can -# either give multiple identifier separated by comma (,) or put this option -# multiple time (only on the command line, not in the configuration file where -# it should appear only once). See also the "--disable" option for examples. -#enable= - -# Disable the message, report, category or checker with the given id(s). You -# can either give multiple identifiers separated by comma (,) or put this -# option multiple times (only on the command line, not in the configuration -# file where it should appear only once).You can also use "--disable=all" to -# disable everything first and then reenable specific checks. For example, if -# you want to run only the similarities checker, you can use "--disable=all -# --enable=similarities". If you want to run only the classes checker, but have -# no Warning level messages displayed, use"--disable=all --enable=classes -# --disable=W" -# RATIONALE: -# - maybe-no-member: bi-modal functions confuse pylint type inference. -# - no-member: indirections in protobuf-generated code -# - protected-access: helpers use '_foo' of classes from generated code. -# - similarities: 'Bucket' and 'Blob' define 'metageneration' and 'owner' with -# identical implementation but different docstrings. -# - star-args: standard Python idioms for varargs: -# ancestor = Query().filter(*order_props) -# - redefined-variable-type: This error is overzealous and complains at e.g. -# if some_prop: -# return int(value) -# else: -# return float(value) -# - import-error: imports are checked via tests. -# - wrong-import-position: This error is overzealous. It assumes imports are -# completed whenever something non-trivial is -# defined, e.g. -# try: -# from foo import Bar -# except ImportError: -# class Bar(object): -# """Hi everyone""" -# and thus causes subsequent imports to be -# diagnosed as out-of-order. -# - no-name-in-module: Error gives a lot of false positives for names which -# are defined dynamically. Also, any truly missing names -# will be detected by our 100% code coverage. -# - missing-raises-doc: New in PyLint 1.6, enforcing PEP 257 and flagged our -# existing codebase. See #1968 for eventual fixes. -# - ungrouped-imports: We share the 'google' namespace with third-party -# packages. PEP 8 wants those to be separate from "local" -# imports. -disable = - import-star-module-level, - old-octal-literal, - oct-method, - print-statement, - unpacking-in-except, - parameter-unpacking, - backtick, - old-raise-syntax, - old-ne-operator, - long-suffix, - dict-view-method, - dict-iter-method, - metaclass-assignment, - next-method-called, - raising-string, - indexing-exception, - raw_input-builtin, - long-builtin, - file-builtin, - execfile-builtin, - coerce-builtin, - cmp-builtin, - buffer-builtin, - basestring-builtin, - apply-builtin, - filter-builtin-not-iterating, - using-cmp-argument, - useless-suppression, - range-builtin-not-iterating, - suppressed-message, - no-absolute-import, - old-division, - cmp-method, - reload-builtin, - zip-builtin-not-iterating, - intern-builtin, - unichr-builtin, - reduce-builtin, - standarderror-builtin, - unicode-builtin, - xrange-builtin, - coerce-method, - delslice-method, - getslice-method, - setslice-method, - input-builtin, - round-builtin, - hex-method, - nonzero-method, - map-builtin-not-iterating, - maybe-no-member, - no-member, - protected-access, - similarities, - star-args, - redefined-variable-type, - import-error, - wrong-import-position, - no-name-in-module, - missing-raises-doc, - ungrouped-imports, - -[REPORTS] - -# Set the output format. Available formats are text, parseable, colorized, msvs -# (visual studio) and html. You can also give a reporter class, eg -# mypackage.mymodule.MyReporterClass. -output-format=text - -# Put messages in a separate file for each module / package specified on the -# command line instead of printing them on stdout. Reports (if any) will be -# written in a file name "pylint_global.[txt|html]". This option is deprecated -# and it will be removed in Pylint 2.0. -files-output=no - -# Tells whether to display a full report or only the messages -# DEFAULT: reports=yes -# RATIONALE: run from Travis / tox, and don't need / want to parse output. -reports=no - -# Python expression which should return a note less than 10 (10 is the highest -# note). You have access to the variables errors warning, statement which -# respectively contain the number of errors / warnings messages and the total -# number of statements analyzed. This is used by the global evaluation report -# (RP0004). -evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) - -# Template used to display messages. This is a python new-style format string -# used to format the message information. See doc for all details -#msg-template= - - -[VARIABLES] - -# Tells whether we should check for unused import in __init__ files. -init-import=no - -# A regular expression matching the name of dummy variables (i.e. expectedly -# not used). -dummy-variables-rgx=(_+[a-zA-Z0-9]*?$)|dummy - -# List of additional names supposed to be defined in builtins. Remember that -# you should avoid to define new builtins when possible. -additional-builtins= - -# List of strings which can identify a callback function by name. A callback -# name must start or end with one of those strings. -callbacks=cb_,_cb - -# List of qualified module names which can have objects that can redefine -# builtins. -redefining-builtins-modules=six.moves,future.builtins - - -[SPELLING] - -# Spelling dictionary name. Available dictionaries: none. To make it working -# install python-enchant package. -spelling-dict= - -# List of comma separated words that should not be checked. -spelling-ignore-words= - -# A path to a file that contains private dictionary; one word per line. -spelling-private-dict-file= - -# Tells whether to store unknown words to indicated private dictionary in -# --spelling-private-dict-file option instead of raising a message. -spelling-store-unknown-words=no - - -[LOGGING] - -# Logging modules to check that the string format arguments are in logging -# function parameter format -logging-modules=logging - - -[BASIC] - -# Good variable names which should always be accepted, separated by a comma -# DEFAULT: good-names=i,j,k,ex,Run,_ -# RATIONALE: 'pb' and 'id' have well-understood meainings in the code. -good-names = i, j, k, ex, Run, _, - pb, - id, - -# Bad variable names which should always be refused, separated by a comma -bad-names=foo,bar,baz,toto,tutu,tata - -# Colon-delimited sets of names that determine each other's naming style when -# the name regexes allow several styles. -name-group= - -# Include a hint for the correct naming format with invalid-name -include-naming-hint=no - -# List of decorators that produce properties, such as abc.abstractproperty. Add -# to this list to register other decorators that produce valid properties. -property-classes=abc.abstractproperty - -# Regular expression matching correct function names -function-rgx=[a-z_][a-z0-9_]{2,30}$ - -# Naming hint for function names -function-name-hint=[a-z_][a-z0-9_]{2,30}$ - -# Regular expression matching correct variable names -variable-rgx=[a-z_][a-z0-9_]{2,30}$ - -# Naming hint for variable names -variable-name-hint=[a-z_][a-z0-9_]{2,30}$ - -# Regular expression matching correct constant names -const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$ - -# Naming hint for constant names -const-name-hint=(([A-Z_][A-Z0-9_]*)|(__.*__))$ - -# Regular expression matching correct attribute names -attr-rgx=[a-z_][a-z0-9_]{2,30}$ - -# Naming hint for attribute names -attr-name-hint=[a-z_][a-z0-9_]{2,30}$ - -# Regular expression matching correct argument names -argument-rgx=[a-z_][a-z0-9_]{2,30}$ - -# Naming hint for argument names -argument-name-hint=[a-z_][a-z0-9_]{2,30}$ - -# Regular expression matching correct class attribute names -class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ - -# Naming hint for class attribute names -class-attribute-name-hint=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ - -# Regular expression matching correct inline iteration names -inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ - -# Naming hint for inline iteration names -inlinevar-name-hint=[A-Za-z_][A-Za-z0-9_]*$ - -# Regular expression matching correct class names -class-rgx=[A-Z_][a-zA-Z0-9]+$ - -# Naming hint for class names -class-name-hint=[A-Z_][a-zA-Z0-9]+$ - -# Regular expression matching correct module names -module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ - -# Naming hint for module names -module-name-hint=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ - -# Regular expression matching correct method names -# DEFAULT: method-rgx=[a-z_][a-z0-9_]{2,30}$ -# RATIONALE: mapping long API names onto connection methods -method-rgx=[a-z_][a-z0-9_]{2,35}$ - -# Naming hint for method names -method-name-hint=[a-z_][a-z0-9_]{2,30}$ - -# Regular expression which should only match function or class names that do -# not require a docstring. -# DEFAULT: no-docstring-rgx=^_ -# RATIONALE: More documentation, even for non-public methods, is useful. -no-docstring-rgx=__.*__ - -# Minimum line length for functions/classes that require docstrings, shorter -# ones are exempt. -docstring-min-length=-1 - - -[ELIF] - -# Maximum number of nested blocks for function / method body -max-nested-blocks=5 - - -[SIMILARITIES] - -# Minimum lines number of a similarity. -min-similarity-lines=4 - -# Ignore comments when computing similarities. -ignore-comments=yes - -# Ignore docstrings when computing similarities. -ignore-docstrings=yes - -# Ignore imports when computing similarities. -ignore-imports=no - - -[FORMAT] - -# Maximum number of characters on a single line. -# DEFAULT: max-line-length=100 -# RATIONALE: PEP8 -max-line-length=80 - -# Regexp for a line that is allowed to be longer than the limit. -ignore-long-lines=^\s*(# )??$ - -# Allow the body of an if to be on the same line as the test if there is no -# else. -single-line-if-stmt=no - -# List of optional constructs for which whitespace checking is disabled. `dict- -# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}. -# `trailing-comma` allows a space between comma and closing bracket: (a, ). -# `empty-line` allows space-only lines. -# DEFAULT: no-space-check=trailing-comma,dict-separator -# RATIONALE: pylint ignores whitespace checks around the -# constructs "dict-separator" (cases like {1:2}) and -# "trailing-comma" (cases like {1: 2, }). -# By setting "no-space-check" to empty whitespace checks will be -# enforced around both constructs. -no-space-check = - -# Maximum number of lines in a module -# DEFAULT: max-module-lines=1000 -# RATIONALE: API-mapping -max-module-lines=1200 - -# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 -# tab). -indent-string=' ' - -# Number of spaces of indent required inside a hanging or continued line. -indent-after-paren=4 - -# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. -# DEFAULT: expected-line-ending-format= -# RATIONALE: Uniform UNIX line endings. -expected-line-ending-format=LF - - -[TYPECHECK] - -# Tells whether missing members accessed in mixin class should be ignored. A -# mixin class is detected if its name ends with "mixin" (case insensitive). -ignore-mixin-members=yes - -# List of module names for which member attributes should not be checked -# (useful for modules/projects where namespaces are manipulated during runtime -# and thus existing member attributes cannot be deduced by static analysis. It -# supports qualified module names, as well as Unix pattern matching. -# DEFAULT: ignored-modules= -# RATIONALE: six aliases stuff for compatibility. -# google.protobuf fixes up namespace package "late". -ignored-modules = six, google.protobuf - -# List of class names for which member attributes should not be checked (useful -# for classes with dynamically set attributes). This supports the use of -# qualified names. -ignored-classes=optparse.Values,thread._local,_thread._local - -# List of members which are set dynamically and missed by pylint inference -# system, and so shouldn't trigger E1101 when accessed. Python regular -# expressions are accepted. -generated-members= - -# List of decorators that produce context managers, such as -# contextlib.contextmanager. Add to this list to register other decorators that -# produce valid context managers. -contextmanager-decorators=contextlib.contextmanager - - -[MISCELLANEOUS] - -# List of note tags to take in consideration, separated by a comma. -notes=FIXME,XXX,TODO - - -[IMPORTS] - -# Deprecated modules which should not be used, separated by a comma -deprecated-modules=regsub,TERMIOS,Bastion,rexec - -# Create a graph of every (i.e. internal and external) dependencies in the -# given file (report RP0402 must not be disabled) -import-graph= - -# Create a graph of external dependencies in the given file (report RP0402 must -# not be disabled) -ext-import-graph= - -# Create a graph of internal dependencies in the given file (report RP0402 must -# not be disabled) -int-import-graph= - -# Force import order to recognize a module as part of the standard -# compatibility libraries. -known-standard-library= - -# Force import order to recognize a module as part of a third party library. -known-third-party=enchant - -# Analyse import fallback blocks. This can be used to support both Python 2 and -# 3 compatible code, which means that the block might have code that exists -# only in one or another interpreter, leading to false positives when analysed. -analyse-fallback-blocks=no - - -[DESIGN] - -# Maximum number of arguments for function / method -# DEFAULT: max-args=5 -# RATIONALE: API-mapping -max-args = 10 - -# Argument names that match this expression will be ignored. Default to name -# with leading underscore -ignored-argument-names=_.* - -# Maximum number of locals for function / method body -# DEFAULT: max-locals=15 -# RATIONALE: Extra thorny code with more locals. -max-locals=20 - -# Maximum number of return / yield for function / method body -max-returns=6 - -# Maximum number of branch for function / method body -max-branches=12 - -# Maximum number of statements in function / method body -max-statements=50 - -# Maximum number of parents for a class (see R0901). -max-parents=7 - -# Maximum number of attributes for a class (see R0902). -# DEFAULT: max-attributes=7 -# RATIONALE: API mapping -max-attributes=15 - -# Minimum number of public methods for a class (see R0903). -# DEFAULT: min-public-methods=2 -# RATIONALE: context mgrs may have *no* public methods -min-public-methods=0 - -# Maximum number of public methods for a class (see R0904). -# DEFAULT: max-public-methods=20 -# RATIONALE: API mapping -max-public-methods=40 - -# Maximum number of boolean expressions in a if statement -max-bool-expr=5 - - -[CLASSES] - -# List of method names used to declare (i.e. assign) instance attributes. -defining-attr-methods=__init__,__new__,setUp - -# List of valid names for the first argument in a class method. -valid-classmethod-first-arg=cls - -# List of valid names for the first argument in a metaclass class method. -valid-metaclass-classmethod-first-arg=mcs - -# List of member names, which should be excluded from the protected access -# warning. -exclude-protected=_asdict,_fields,_replace,_source,_make - - -[EXCEPTIONS] - -# Exceptions that will emit a warning when being caught. Defaults to -# "Exception" -overgeneral-exceptions=Exception diff --git a/scripts/rewrite_imports.py b/scripts/rewrite_imports.py deleted file mode 100644 index 2b2e1ac2146c..000000000000 --- a/scripts/rewrite_imports.py +++ /dev/null @@ -1,146 +0,0 @@ -# Copyright 2015 Google Inc. -# -# Licensed 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. - -"""Build script for rewriting imports for protobuf generated modules. - -Intended to be used for Google Cloud Bigtable protos (google/bigtable/v1) -and the dependent modules (google/api and google/protobuf). -""" - -import sys - - -IMPORT_TEMPLATE = 'import %s' -IMPORT_FROM_TEMPLATE = 'from %s import ' -REPLACEMENTS = { - # Bigtable v2 - 'google.bigtable.v2': 'google.cloud.bigtable._generated', - 'google.bigtable.admin.v2': 'google.cloud.bigtable._generated', -} - - -def transform_old_to_new(line, old_module, new_module, - ignore_import_from=False): - """Transforms from an old module to a new one. - - First checks if a line starts with - "from {old_module} import ..." - then checks if the line contains - "import {old_module} ..." - then checks if the line starts with (ignoring whitespace) - "{old_module} ..." - and finally checks if the line contians - "'some-dict-key': {old_module} ..." - - In any of these cases, "{old_module}" is replaced with "{new_module}". - If none match, nothing is returned. - - :type line: str - :param line: The line to be transformed. - - :type old_module: str - :param old_module: The import to be re-written. - - :type new_module: str - :param new_module: The new location of the re-written import. - - :type ignore_import_from: bool - :param ignore_import_from: Flag to determine if the "from * import" - statements should be ignored. - - :rtype: :class:`str` or :data:`NoneType ` - :returns: The transformed line if the old module was found, otherwise - does nothing. - """ - if not ignore_import_from: - import_from_statement = IMPORT_FROM_TEMPLATE % (old_module,) - if line.startswith(import_from_statement): - new_import_from_statement = IMPORT_FROM_TEMPLATE % (new_module,) - # Only replace the first instance of the import statement. - return line.replace(import_from_statement, - new_import_from_statement, 1) - - # If the line doesn't start with a "from * import *" statement, it - # may still contain a "import * ..." statement. - import_statement = IMPORT_TEMPLATE % (old_module,) - if import_statement in line: - new_import_statement = IMPORT_TEMPLATE % (new_module,) - # Only replace the first instance of the import statement. - return line.replace(import_statement, - new_import_statement, 1) - - # Also need to change references to the standalone imports. As a - # stop-gap we fix references to them at the beginning of a line - # (ignoring whitespace). - if line.lstrip().startswith(old_module): - # Only replace the first instance of the old_module. - return line.replace(old_module, new_module, 1) - - # Finally check for usage in dictionaries. - if ': ' + old_module in line: - # Only replace the first instance of the old_module. - return line.replace(': ' + old_module, ': ' + new_module, 1) - - -def transform_line(line): - """Transforms an import line in a PB2 module. - - If the line is not an import of one of the packages in ``REPLACEMENTS``, - does nothing and returns the original. Otherwise it replaces the package - matched with our local package. - - :type line: str - :param line: The line to be transformed. - - :rtype: str - :returns: The transformed line. - """ - # Work around https://github.com/grpc/grpc/issues/7101 - if line == 'import ': - return '' - - for old_module, new_module in REPLACEMENTS.iteritems(): - result = transform_old_to_new(line, old_module, new_module) - if result is not None: - return result - - # If no matches, there is nothing to transform. - return line - - -def rewrite_file(filename): - """Rewrites a given PB2 modules. - - :type filename: str - :param filename: The name of the file to be rewritten. - """ - with open(filename, 'rU') as file_obj: - content_lines = file_obj.read().split('\n') - - new_content = [] - for line in content_lines: - new_content.append(transform_line(line)) - - with open(filename, 'w') as file_obj: - file_obj.write('\n'.join(new_content)) - - -def main(): - """Rewrites all PB2 files.""" - for filename in sys.argv[1:]: - rewrite_file(filename) - - -if __name__ == '__main__': - main() diff --git a/scripts/run_pylint.py b/scripts/run_pylint.py deleted file mode 100644 index f870df71bfd3..000000000000 --- a/scripts/run_pylint.py +++ /dev/null @@ -1,202 +0,0 @@ -# Copyright 2014 Google Inc. -# -# Licensed 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. - -"""Custom script to run PyLint on google-cloud codebase. - -This runs pylint as a script via subprocess in two different -subprocesses. The first lints the production/library code -using the default rc file (PRODUCTION_RC). The second lints the -test code using an rc file (TEST_RC) which allows more style -violations (hence it has a reduced number of style checks). -""" - -from __future__ import print_function - -import ConfigParser -import copy -import os -import subprocess -import sys - -from script_utils import get_affected_files -from script_utils import PROJECT_ROOT - - -IGNORED_DIRECTORIES = [ - os.path.join('bigtable', 'google', 'cloud', 'bigtable', '_generated'), -] -IGNORED_FILES = [ - os.path.join('docs', 'conf.py'), -] -IGNORED_POSTFIXES = [ - os.path.join('google', '__init__.py'), - os.path.join('google', 'cloud', '__init__.py'), - 'setup.py', -] -SCRIPTS_DIR = os.path.join(PROJECT_ROOT, 'scripts') -PRODUCTION_RC = os.path.join(SCRIPTS_DIR, 'pylintrc_default') -TEST_RC = os.path.join(SCRIPTS_DIR, 'pylintrc_reduced') -TEST_DISABLED_MESSAGES = [ - 'abstract-method', - 'arguments-differ', - 'assignment-from-no-return', - 'attribute-defined-outside-init', - 'exec-used', - 'import-error', - 'invalid-name', - 'missing-docstring', - 'no-init', - 'no-self-use', - 'superfluous-parens', - 'too-few-public-methods', - 'too-many-locals', - 'too-many-public-methods', - 'unbalanced-tuple-unpacking', -] -TEST_RC_ADDITIONS = { - 'MESSAGES CONTROL': { - 'disable': ', '.join(TEST_DISABLED_MESSAGES), - }, -} -TEST_RC_REPLACEMENTS = { - 'FORMAT': { - 'max-module-lines': 2050, - }, -} - - -def read_config(filename): - """Reads pylintrc config onto native ConfigParser object.""" - config = ConfigParser.ConfigParser() - with open(filename, 'r') as file_obj: - config.readfp(file_obj) - return config - - -def make_test_rc(base_rc_filename, additions_dict, - replacements_dict, target_filename): - """Combines a base rc and test additions into single file.""" - main_cfg = read_config(base_rc_filename) - - # Create fresh config for test, which must extend production. - test_cfg = ConfigParser.ConfigParser() - test_cfg._sections = copy.deepcopy(main_cfg._sections) - - for section, opts in additions_dict.items(): - curr_section = test_cfg._sections.setdefault( - section, test_cfg._dict()) - for opt, opt_val in opts.items(): - curr_val = curr_section.get(opt) - if curr_val is None: - raise KeyError('Expected to be adding to existing option.') - curr_val = curr_val.rstrip(',') - curr_section[opt] = '%s, %s' % (curr_val, opt_val) - - for section, opts in replacements_dict.items(): - curr_section = test_cfg._sections.setdefault( - section, test_cfg._dict()) - for opt, opt_val in opts.items(): - curr_val = curr_section.get(opt) - if curr_val is None: - raise KeyError('Expected to be replacing existing option.') - curr_section[opt] = '%s' % (opt_val,) - - with open(target_filename, 'w') as file_obj: - test_cfg.write(file_obj) - - -def valid_filename(filename): - """Checks if a file is a Python file and is not ignored.""" - for postfix in IGNORED_POSTFIXES: - if filename.endswith(postfix): - return False - for directory in IGNORED_DIRECTORIES: - if filename.startswith(directory): - return False - return (filename.endswith('.py') and - filename not in IGNORED_FILES) - - -def is_production_filename(filename): - """Checks if the file contains production code. - - :rtype: bool - :returns: Boolean indicating production status. - """ - return 'test' not in filename and 'docs' not in filename - - -def get_python_files(all_files=None): - """Gets a list of all Python files in the repository that need linting. - - Relies on :func:`get_affected_files()` to determine which files should - be considered. - - NOTE: This requires ``git`` to be installed and requires that this - is run within the ``git`` repository. - - :type all_files: list - :param all_files: Optional list of files to be linted. - - :rtype: tuple - :returns: A tuple containing two lists. The first list - contains all production files, the next all test files. - """ - if all_files is None: - all_files, diff_base = get_affected_files() - - library_files = [] - non_library_files = [] - for filename in all_files: - if valid_filename(filename): - if is_production_filename(filename): - library_files.append(filename) - else: - non_library_files.append(filename) - - return library_files, non_library_files, diff_base - - -def lint_fileset(filenames, rcfile, description): - """Lints a group of files using a given rcfile.""" - if filenames: - rc_flag = '--rcfile=%s' % (rcfile,) - pylint_shell_command = ['pylint', rc_flag] - errors = {} # filename -> status_code - for filename in filenames: - cmd = pylint_shell_command + [filename] - status_code = subprocess.call(cmd) - if status_code != 0: - errors[filename] = status_code - if errors: - for filename, status_code in sorted(errors.items()): - print('%-30s: %d' % (filename, status_code), file=sys.stderr) - sys.exit(len(errors)) - else: - print('Skipping %s, no files to lint.' % (description,)) - - -def main(): - """Script entry point. Lints both sets of files.""" - make_test_rc(PRODUCTION_RC, TEST_RC_ADDITIONS, - TEST_RC_REPLACEMENTS, TEST_RC) - library_files, non_library_files, diff_base = get_python_files() - if diff_base: - print('Checking only files which differ from base.') - lint_fileset(library_files, PRODUCTION_RC, 'library code') - lint_fileset(non_library_files, TEST_RC, 'test code') - - -if __name__ == '__main__': - main() diff --git a/scripts/run_unit_tests.py b/scripts/run_unit_tests.py deleted file mode 100644 index 8ff3d20a984b..000000000000 --- a/scripts/run_unit_tests.py +++ /dev/null @@ -1,267 +0,0 @@ -# Copyright 2016 Google Inc. -# -# Licensed 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. - -"""Script to run unit tests for the entire project. - -This script orchestrates stepping into each sub-package and -running tests. It also allows running a limited subset of tests -in cases where such a limited subset can be identified. -""" - -from __future__ import print_function - -import argparse -import os -import subprocess -import sys - -from script_utils import check_output -from script_utils import follow_dependencies -from script_utils import get_changed_packages -from script_utils import in_travis -from script_utils import in_travis_pr -from script_utils import local_diff_branch -from script_utils import PROJECT_ROOT -from script_utils import travis_branch - - -IGNORED_DIRECTORIES = ( - '.github', - 'appveyor', - 'docs', - 'scripts', - 'system_tests', -) -TOX_ENV_VAR = 'TOXENV' -ACCEPTED_VERSIONS = { - (2, 7): 'py27', - (3, 4): 'py34', - (3, 5): 'py35', -} -UNSET_SENTINEL = object() # Sentinel for argparser - - -def get_package_directories(): - """Get a list of directories containing sub-packages. - - :rtype: list - :returns: A list of all sub-package directories. - """ - # Run ls-tree with - # -d: For directories only - # --name-only: No extra info like the type/hash/permission bits. - # --full-name: Give path relative to root, rather than cwd. - ls_tree_out = check_output( - 'git', 'ls-tree', - '-d', '--name-only', '--full-name', - 'HEAD', PROJECT_ROOT) - result = [] - for package in ls_tree_out.split('\n'): - if package not in IGNORED_DIRECTORIES: - result.append(package) - return result - - -def get_travis_directories(package_list): - """Get list of packages that need to be tested on Travis CI. - - See: https://travis-ci.com/ - - If the current Travis build is for a pull request (PR), this will - limit the directories to the ones impacted by the PR. Otherwise - it will just test all package directories. - - :type package_list: list - :param package_list: The list of **all** valid packages with unit tests. - - :rtype: list - :returns: A list of all package directories where tests - need to be run. - """ - if in_travis_pr(): - pr_against_branch = travis_branch() - return get_changed_packages('HEAD', pr_against_branch, - package_list) - else: - return package_list - - -def verify_packages(subset, all_packages): - """Verify that a subset of packages are among all packages. - - :type subset: list - :param subset: List of a subset of package names. - - :type all_packages: list - :param all_packages: List of all package names. - - :raises: :class:`~exceptions.ValueError` if there are unknown packages - in ``subset`` - """ - left_out = set(subset) - set(all_packages) - if left_out: - raise ValueError('Unknown packages', - sorted(left_out)) - - -def get_test_packages(): - """Get a list of packages which need tests run. - - Filters the package list in the following order: - - * Check command line for packages passed in as positional arguments - * Check if the the local remote and local branch environment variables - have been set to specify a remote branch to diff against. - * Check if in Travis, then limit the subset based on changes - in a Pull Request ("push" builds to branches may not have - any filtering) - * Just use all packages - - An additional check is done for the cases when a diff is computed (i.e. - using local remote and local branch environment variables, and on Travis). - Once the filtered list of **changed** packages is found, the package - dependency graph is used to add any additional packages which depend on - the changed packages. - - :rtype: list - :returns: A list of all package directories where tests - need be run. - """ - all_packages = get_package_directories() - local_diff = local_diff_branch() - - parser = get_parser() - args = parser.parse_args() - if args.packages is not UNSET_SENTINEL: - verify_packages(args.packages, all_packages) - return sorted(args.packages) - elif local_diff is not None: - changed_packages = get_changed_packages( - 'HEAD', local_diff, all_packages) - return follow_dependencies(changed_packages, all_packages) - elif in_travis(): - changed_packages = get_travis_directories(all_packages) - return follow_dependencies(changed_packages, all_packages) - else: - return all_packages - - -def run_package(package, tox_env): - """Run tox environment for a given package. - - :type package: str - :param package: The name of the subdirectory which holds the sub-package. - This will be a path relative to ``PROJECT_ROOT``. - - :type tox_env: str - :param tox_env: The ``tox`` environment(s) to run in each sub-package. - - :rtype: bool - :returns: Flag indicating if the test run succeeded. - """ - curr_dir = os.getcwd() - package_dir = os.path.join(PROJECT_ROOT, package) - try: - os.chdir(package_dir) - return_code = subprocess.call(['tox', '-e', tox_env]) - return return_code == 0 - finally: - os.chdir(curr_dir) - - -def get_parser(): - """Get simple ``argparse`` parser to determine configuration. - - :rtype: :class:`argparse.ArgumentParser` - :returns: The parser for this script. - """ - description = 'Run tox environment(s) in all sub-packages.' - parser = argparse.ArgumentParser(description=description) - parser.add_argument( - '--tox-env', dest='tox_env', - help='The tox environment(s) to run in sub-packages.') - packages_help = 'Optional list of sub-packages to be tested.' - parser.add_argument('packages', nargs='*', - default=UNSET_SENTINEL, help=packages_help) - return parser - - -def get_tox_env_from_version(): - """Get ``tox`` environment from the current Python version. - - :rtype: str - :returns: The current ``tox`` environment to be used, e.g. ``"py27"``. - :raises: :class:`EnvironmentError` if the first two options - don't yield any value and the current version of - Python is not in ``ACCEPTED_VERSIONS``. - """ - version_info = sys.version_info[:2] - try: - return ACCEPTED_VERSIONS[version_info] - except KeyError: - raise EnvironmentError( - 'Invalid Python version', version_info, - 'Accepted versions are', - sorted(ACCEPTED_VERSIONS.keys())) - - -def get_tox_env(): - """Get the environment to be used with ``tox``. - - Tries to infer the ``tox`` environment in the following order - - * From the ``--tox-env`` command line flag - * From the ``TOXENV`` environment variable - * From the version of the current running Python - - :rtype: str - :returns: The current ``tox`` environment to be used, e.g. ``"py27"``. - """ - parser = get_parser() - args = parser.parse_args() - if args.tox_env is not None: - tox_env = args.tox_env - elif TOX_ENV_VAR in os.environ: - tox_env = os.environ[TOX_ENV_VAR] - else: - tox_env = get_tox_env_from_version() - - return tox_env - - -def main(): - """Run all the unit tests that need to be run.""" - packages_to_run = get_test_packages() - if not packages_to_run: - print('No tests to run.') - return - - tox_env = get_tox_env() - failed_packages = [] - for package in packages_to_run: - succeeded = run_package(package, tox_env) - if not succeeded: - failed_packages.append(package) - - if failed_packages: - msg_parts = ['Sub-packages failed:'] - for package in failed_packages: - msg_parts.append('- ' + package) - msg = '\n'.join(msg_parts) - print(msg, file=sys.stderr) - sys.exit(1) - - -if __name__ == '__main__': - main() diff --git a/scripts/script_utils.py b/scripts/script_utils.py deleted file mode 100644 index 5cb0258e6a66..000000000000 --- a/scripts/script_utils.py +++ /dev/null @@ -1,352 +0,0 @@ -# Copyright 2016 Google Inc. -# -# Licensed 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. - -"""Common helpers for testing scripts.""" - -from __future__ import print_function - -import ast -import os -import subprocess - - -PROJECT_ROOT = os.path.abspath( - os.path.join(os.path.dirname(__file__), '..')) -LOCAL_REMOTE_ENV = 'GOOGLE_CLOUD_TESTING_REMOTE' -LOCAL_BRANCH_ENV = 'GOOGLE_CLOUD_TESTING_BRANCH' -IN_TRAVIS_ENV = 'TRAVIS' -TRAVIS_PR_ENV = 'TRAVIS_PULL_REQUEST' -TRAVIS_BRANCH_ENV = 'TRAVIS_BRANCH' -INST_REQS_KWARG = 'install_requires' -REQ_VAR = 'REQUIREMENTS' -PACKAGE_PREFIX = 'google-cloud-' - - -def in_travis(): - """Detect if we are running in Travis. - - .. _Travis env docs: https://docs.travis-ci.com/user/\ - environment-variables\ - #Default-Environment-Variables - - See `Travis env docs`_. - - :rtype: bool - :returns: Flag indicating if we are running on Travis. - """ - return os.getenv(IN_TRAVIS_ENV) == 'true' - - -def in_travis_pr(): - """Detect if we are running in a pull request on Travis. - - .. _Travis env docs: https://docs.travis-ci.com/user/\ - environment-variables\ - #Default-Environment-Variables - - See `Travis env docs`_. - - .. note:: - - This assumes we already know we are running in Travis. - - :rtype: bool - :returns: Flag indicating if we are in a pull request on Travis. - """ - # NOTE: We're a little extra cautious and make sure that the - # PR environment variable is an integer. - try: - int(os.getenv(TRAVIS_PR_ENV, '')) - return True - except ValueError: - return False - - -def travis_branch(): - """Get the current branch of the PR. - - .. _Travis env docs: https://docs.travis-ci.com/user/\ - environment-variables\ - #Default-Environment-Variables - - See `Travis env docs`_. - - .. note:: - - This assumes we already know we are running in Travis - during a PR. - - :rtype: str - :returns: The name of the branch the current pull request is - changed against. - :raises: :class:`~exceptions.OSError` if the ``TRAVIS_BRANCH_ENV`` - environment variable isn't set during a pull request - build. - """ - try: - return os.environ[TRAVIS_BRANCH_ENV] - except KeyError: - msg = ('Pull request build does not have an ' - 'associated branch set (via %s)') % (TRAVIS_BRANCH_ENV,) - raise OSError(msg) - - -def check_output(*args): - """Run a command on the operation system. - - :type args: tuple - :param args: Arguments to pass to ``subprocess.check_output``. - - :rtype: str - :returns: The raw STDOUT from the command (converted from bytes - if necessary). - """ - cmd_output = subprocess.check_output(args) - # On Python 3, this returns bytes (from STDOUT), so we - # convert to a string. - cmd_output = cmd_output.decode('utf-8') - # Also strip the output since it usually has a trailing newline. - return cmd_output.strip() - - -def rootname(filename): - """Get the root directory that a file is contained in. - - :type filename: str - :param filename: The path / name of a file. - - :rtype: str - :returns: The root directory containing the file. - """ - if os.path.sep not in filename: - return '' - else: - file_root, _ = filename.split(os.path.sep, 1) - return file_root - - -def get_changed_packages(blob_name1, blob_name2, package_list): - """Get a list of packages which have changed between two changesets. - - :type blob_name1: str - :param blob_name1: The name of a commit hash or branch name or other - ``git`` artifact. - - :type blob_name2: str - :param blob_name2: The name of a commit hash or branch name or other - ``git`` artifact. - - :type package_list: list - :param package_list: The list of **all** valid packages with unit tests. - - :rtype: list - :returns: A list of all package directories that have changed - between ``blob_name1`` and ``blob_name2``. Starts - with a list of valid packages (``package_list``) - and filters out the unchanged directories. - """ - changed_files = check_output( - 'git', 'diff', '--name-only', blob_name1, blob_name2) - changed_files = changed_files.split('\n') - - result = set() - for filename in changed_files: - file_root = rootname(filename) - if file_root in package_list: - result.add(file_root) - - return sorted(result) - - -def local_diff_branch(): - """Get a remote branch to diff against in a local checkout. - - Checks if the the local remote and local branch environment - variables specify a remote branch. - - :rtype: str - :returns: The diffbase `{remote}/{branch}` if the environment - variables are defined. If not, returns ``None``. - """ - # Only allow specified remote and branch in local dev. - remote = os.getenv(LOCAL_REMOTE_ENV) - branch = os.getenv(LOCAL_BRANCH_ENV) - if remote is not None and branch is not None: - return '%s/%s' % (remote, branch) - - -def get_affected_files(allow_limited=True): - """Gets a list of files in the repository. - - By default, returns all files via ``git ls-files``. However, in some cases - uses a specific commit or branch (a so-called diff base) to compare - against for changed files. (This requires ``allow_limited=True``.) - - To speed up linting on Travis pull requests against master, we manually - set the diff base to the branch the pull request is against. We don't do - this on "push" builds since "master" will be the currently checked out - code. One could potentially use ${TRAVIS_COMMIT_RANGE} to find a diff base - but this value is not dependable. - - To allow faster local ``tox`` runs, the local remote and local branch - environment variables can be set to specify a remote branch to diff - against. - - :type allow_limited: bool - :param allow_limited: Boolean indicating if a reduced set of files can - be used. - - :rtype: pair - :returns: Tuple of the diff base using the list of filenames to be - linted. - """ - diff_base = None - if in_travis(): - # In the case of a pull request into a branch, we want to - # diff against HEAD in that branch. - if in_travis_pr(): - diff_base = travis_branch() - else: - diff_base = local_diff_branch() - - if diff_base is not None and allow_limited: - result = subprocess.check_output(['git', 'diff', '--name-only', - diff_base]) - print('Using files changed relative to %s:' % (diff_base,)) - print('-' * 60) - print(result.rstrip('\n')) # Don't print trailing newlines. - print('-' * 60) - else: - print('Diff base not specified, listing all files in repository.') - result = subprocess.check_output(['git', 'ls-files']) - - # Only return filenames that exist. For example, 'git diff --name-only' - # could spit out deleted / renamed files. Another alternative could - # be to use 'git diff --name-status' and filter out files with a - # status of 'D'. - filenames = [filename - for filename in result.rstrip('\n').split('\n') - if os.path.exists(filename)] - return filenames, diff_base - - -def get_required_packages(file_contents): - """Get required packages from a ``setup.py`` file. - - Makes the following assumptions: - - * ``install_requires=REQUIREMENTS`` occurs in the call to - ``setup()`` in the ``file_contents``. - * The text ``install_requires`` occurs nowhere else in the file. - * The text ``REQUIREMENTS`` only appears when being passed to - ``setup()`` (as above) and when being defined. - * The ``REQUIREMENTS`` variable is a list and the text from the - ``setup.py`` file containing that list can be parsed using - ``ast.literal_eval()``. - - :type file_contents: str - :param file_contents: The contents of a ``setup.py`` file. - - :rtype: list - :returns: The list of required packages. - :raises: :class:`~exceptions.ValueError` if the file is in an - unexpected format. - """ - # Make sure the only ``install_requires`` happens in the - # call to setup() - if file_contents.count(INST_REQS_KWARG) != 1: - raise ValueError('Expected only one use of keyword', - INST_REQS_KWARG, file_contents) - # Make sure the only usage of ``install_requires`` is to set - # install_requires=REQUIREMENTS. - keyword_stmt = INST_REQS_KWARG + '=' + REQ_VAR - if file_contents.count(keyword_stmt) != 1: - raise ValueError('Expected keyword to be set with variable', - INST_REQS_KWARG, REQ_VAR, file_contents) - # Split file on ``REQUIREMENTS`` variable while asserting that - # it only appear twice. - _, reqs_section, _ = file_contents.split(REQ_VAR) - # Find ``REQUIREMENTS`` list variable defined in ``reqs_section``. - reqs_begin = reqs_section.index('[') - reqs_end = reqs_section.index(']') + 1 - - # Convert the text to an actual list, but make sure no - # locals or globals can be used. - reqs_list_text = reqs_section[reqs_begin:reqs_end] - # We use literal_eval() because it limits to evaluating - # strings that only consist of a few Python literals: strings, - # numbers, tuples, lists, dicts, booleans, and None. - requirements = ast.literal_eval(reqs_list_text) - - # Take the list of requirements and strip off the package name - # from each requirement. - result = [] - for required in requirements: - parts = required.split() - result.append(parts[0]) - return result - - -def get_dependency_graph(package_list): - """Get a directed graph of package dependencies. - - :type package_list: list - :param package_list: The list of **all** valid packages. - - :rtype: dict - :returns: A dictionary where keys are packages and values are - the set of packages that depend on the key. - """ - result = {package: set() for package in package_list} - for package in package_list: - setup_file = os.path.join(PROJECT_ROOT, package, - 'setup.py') - with open(setup_file, 'r') as file_obj: - file_contents = file_obj.read() - - requirements = get_required_packages(file_contents) - for requirement in requirements: - if not requirement.startswith(PACKAGE_PREFIX): - continue - _, req_package = requirement.split(PACKAGE_PREFIX) - req_package = req_package.replace('-', '_') - result[req_package].add(package) - - return result - - -def follow_dependencies(subset, package_list): - """Get a directed graph of package dependencies. - - :type subset: list - :param subset: List of a subset of package names. - - :type package_list: list - :param package_list: The list of **all** valid packages. - - :rtype: list - :returns: An expanded list of packages containing everything - in ``subset`` and any packages that depend on those. - """ - dependency_graph = get_dependency_graph(package_list) - - curr_pkgs = None - updated_pkgs = set(subset) - while curr_pkgs != updated_pkgs: - curr_pkgs = updated_pkgs - updated_pkgs = set(curr_pkgs) - for package in curr_pkgs: - updated_pkgs.update(dependency_graph[package]) - - return sorted(curr_pkgs) diff --git a/scripts/update_json_docs.sh b/scripts/update_json_docs.sh deleted file mode 100755 index 5c8658c5fb6f..000000000000 --- a/scripts/update_json_docs.sh +++ /dev/null @@ -1,83 +0,0 @@ -#!/bin/bash - -# Copyright 2016 Google Inc. -# -# Licensed 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. - -set -ev - -GH_OWNER="GoogleCloudPlatform" -GH_PROJECT_NAME="google-cloud-python" - -function buildDocs () { - echo "Building JSON documentation..." - python scripts/generate_json_docs.py --tag ${1} - echo "Done building JSON documentation." -} - - -function pushDocs () { - echo "Deploying JSON documentation..." - if [[ ! -d "ghpages" ]]; then - git submodule add -q -f -b gh-pages https://${GH_OAUTH_TOKEN}@github.com/${GH_OWNER}/${GH_PROJECT_NAME} ghpages - fi - mkdir -p ghpages/json/${1} - cp -R docs/_build/json_build/* ghpages/ - cd ghpages - git add . - if [[ -n "$(git status --porcelain)" ]]; then - git config user.name "travis-ci" - git config user.email "travis@travis-ci.org" - git commit -m "Updating docs for ${1}" - git status - git push -q https://${GH_OAUTH_TOKEN}@github.com/${GH_OWNER}/${GH_PROJECT_NAME} HEAD:gh-pages - else - echo "Nothing to commit." - fi - cd .. - - echo "Done deploying JSON documentation." -} - -function cleanSubmodule () { - echo "Cleaning up!" - git submodule deinit -q -f ghpages - git reset HEAD .gitmodules - git reset HEAD ghpages - rm -rf ghpages - rm -f .gitmodules - rm -rf .git/modules/ghpages - echo "Done cleaning up!" -} - -# Run this to verifiy that the docs build successfully. -DOC_VERSION='master' -if [[ ! -z ${TRAVIS_TAG} ]]; then - DOC_VERSION=${TRAVIS_TAG} -else - DOC_VERSION=${TRAVIS_BRANCH} -fi - -buildDocs ${DOC_VERSION} - -if [ "${TRAVIS_BRANCH}" == "master" ] && [ "${TRAVIS_PULL_REQUEST}" == "false" ]; then - pushDocs ${DOC_VERSION} - cleanSubmodule - echo "Done pushing docsite. See: https://googlecloudplatform.github.io/google-cloud-python/" -fi - -if [[ ! -z ${TRAVIS_TAG} ]]; then - pushDocs ${DOC_VERSION} - cleanSubmodule - echo "Done pushing docsite. See: https://googlecloudplatform.github.io/google-cloud-python/" -fi diff --git a/scripts/verify_included_modules.py b/scripts/verify_included_modules.py deleted file mode 100644 index 5e01643713d4..000000000000 --- a/scripts/verify_included_modules.py +++ /dev/null @@ -1,207 +0,0 @@ -# Copyright 2016 Google Inc. -# -# Licensed 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. - -"""Check if all public modules are included in our docs.""" - - -from __future__ import print_function - -import argparse -import os -import sys -import warnings - -from sphinx.ext.intersphinx import fetch_inventory - -from script_utils import PROJECT_ROOT - - -DOCS_DIR = os.path.join(PROJECT_ROOT, 'docs') -IGNORED_PREFIXES = ('test_', '_') -IGNORED_MODULES = frozenset([ - 'google.cloud', - 'google.cloud.bigquery', - 'google.cloud.bigtable', - 'google.cloud.dns', - 'google.cloud.error_reporting', - 'google.cloud.language', - 'google.cloud.logging', - 'google.cloud.logging.handlers', - 'google.cloud.logging.handlers.transports', - 'google.cloud.monitoring', - 'google.cloud.pubsub', - 'google.cloud.resource_manager', - 'google.cloud.spanner', - 'google.cloud.speech', - 'google.cloud.storage', - 'google.cloud.streaming', - 'google.cloud.streaming.buffered_stream', - 'google.cloud.streaming.exceptions', - 'google.cloud.streaming.http_wrapper', - 'google.cloud.streaming.stream_slice', - 'google.cloud.streaming.transfer', - 'google.cloud.streaming.util', - 'google.cloud.translate', - 'google.cloud.vision', - 'google.cloud.vision.fixtures', -]) -PACKAGES = ( - 'bigquery', - 'bigtable', - 'core', - 'datastore', - 'dns', - 'error_reporting', - 'language', - 'logging', - 'monitoring', - 'pubsub', - 'resource_manager', - 'runtimeconfig', - 'spanner', - 'speech', - 'storage', - 'translate', - 'vision', -) - - -class SphinxApp(object): - """Mock app to interact with Sphinx helpers.""" - warn = warnings.warn - srcdir = DOCS_DIR - - -def is_valid_module(filename): - """Determines if a filename is a valid Python module. - - Assumes if is just the end of a path (i.e. does not contain - ``os.path.sep``. - - :type filename: str - :param filename: The name of a file. - - :rtype: bool - :returns: Flag indicating if the filename is valid. - """ - if not filename.endswith('.py'): - return False - if filename == '__init__.py': - return True - for prefix in IGNORED_PREFIXES: - if filename.startswith(prefix): - return False - return True - - -def get_public_modules(path, base_package=None): - """Get list of all public modules relative to a path. - - :type path: str - :param path: The path containing the python modules. - - :type base_package: str - :param base_package: (Optional) A package to prepend in - front of the path. - - :rtype: list - :returns: List of all modules found. - """ - result = [] - for subdir, _, files in os.walk(path): - # Skip folders that start with _. - if any([part.startswith('_') - for part in subdir.split(os.path.sep)]): - continue - _, rel_dir = subdir.split(path) - rel_dir = rel_dir.lstrip(os.path.sep) - for filename in files: - if is_valid_module(filename): - mod_name, _ = os.path.splitext(filename) - rel_path = os.path.join(rel_dir, mod_name) - if base_package is not None: - rel_path = os.path.join(base_package, rel_path) - # Turn into a Python module rather than a file path. - rel_path = rel_path.replace(os.path.sep, '.') - if mod_name == '__init__': - result.append(rel_path[:-len('.__init__')]) - else: - result.append(rel_path) - - return result - - -def verify_modules(build_root='_build'): - """Verify modules included. - - :type build_root: str - :param build_root: The root of the directory where docs are built into. - Defaults to ``_build``. - """ - object_inventory_relpath = os.path.join(build_root, 'html', 'objects.inv') - - mock_uri = '' - inventory = fetch_inventory(SphinxApp, mock_uri, - object_inventory_relpath) - sphinx_mods = set(inventory['py:module'].keys()) - - public_mods = set() - for package in PACKAGES: - library_dir = os.path.join(PROJECT_ROOT, package, 'google', 'cloud') - package_mods = get_public_modules(library_dir, - base_package='google.cloud') - public_mods.update(package_mods) - - if not sphinx_mods <= public_mods: - unexpected_mods = sphinx_mods - public_mods - message = ['Unexpected error. There were modules referenced by ' - 'Sphinx that are not among the public modules.'] - message.extend(['- %s' % (mod,) for mod in unexpected_mods]) - print('\n'.join(message), file=sys.stderr) - sys.exit(1) - - undocumented_mods = public_mods - sphinx_mods - # Remove ignored modules. - undocumented_mods -= IGNORED_MODULES - if undocumented_mods: - message_parts = ['Found undocumented public modules:'] - message_parts.extend(['- ' + mod_name - for mod_name in sorted(undocumented_mods)]) - print('\n'.join(message_parts), file=sys.stderr) - sys.exit(1) - - -def get_parser(): - """Get simple ``argparse`` parser to determine package. - - :rtype: :class:`argparse.ArgumentParser` - :returns: The parser for this script. - """ - description = ('Run check that all google-cloud ' - 'modules are included in docs.') - parser = argparse.ArgumentParser(description=description) - parser.add_argument('--build-root', dest='build_root', - help='The root directory where docs are located.') - return parser - - -def main(): - """Main script to verify modules included.""" - parser = get_parser() - args = parser.parse_args() - verify_modules(build_root=args.build_root) - - -if __name__ == '__main__': - main() diff --git a/spanner/.flake8 b/spanner/.flake8 new file mode 100644 index 000000000000..25168dc87605 --- /dev/null +++ b/spanner/.flake8 @@ -0,0 +1,6 @@ +[flake8] +exclude = + __pycache__, + .git, + *.pyc, + conf.py diff --git a/spanner/LICENSE b/spanner/LICENSE new file mode 100644 index 000000000000..d64569567334 --- /dev/null +++ b/spanner/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed 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. diff --git a/spanner/MANIFEST.in b/spanner/MANIFEST.in index cb3a2b9ef4fa..9f7100c9528a 100644 --- a/spanner/MANIFEST.in +++ b/spanner/MANIFEST.in @@ -1,4 +1,4 @@ -include README.rst -graft google -graft unit_tests -global-exclude *.pyc +include README.rst LICENSE +recursive-include google *.json *.proto +recursive-include unit_tests * +global-exclude *.pyc __pycache__ diff --git a/spanner/google/cloud/spanner/__init__.py b/spanner/google/cloud/spanner/__init__.py index 25e8ec04c238..31913d8b1202 100644 --- a/spanner/google/cloud/spanner/__init__.py +++ b/spanner/google/cloud/spanner/__init__.py @@ -27,3 +27,7 @@ from google.cloud.spanner.pool import AbstractSessionPool from google.cloud.spanner.pool import BurstyPool from google.cloud.spanner.pool import FixedSizePool + + +__all__ = ['__version__', 'AbstractSessionPool', 'BurstyPool', 'Client', + 'FixedSizePool', 'KeyRange', 'KeySet'] diff --git a/spanner/nox.py b/spanner/nox.py new file mode 100644 index 000000000000..0ef56fb2803d --- /dev/null +++ b/spanner/nox.py @@ -0,0 +1,87 @@ +# Copyright 2016 Google Inc. +# +# Licensed 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. + +from __future__ import absolute_import + +import os + +import nox + + +@nox.session +@nox.parametrize('python_version', ['2.7', '3.4', '3.5', '3.6']) +def unit_tests(session, python_version): + """Run the unit test suite.""" + + # Run unit tests against all supported versions of Python. + session.interpreter = 'python%s' % python_version + + # Install all test dependencies, then install this package in-place. + session.install('mock', 'pytest', 'pytest-cov', '../core/') + session.install('-e', '.') + + # Run py.test against the unit tests. + session.run('py.test', '--quiet', + '--cov=google.cloud.spanner', '--cov=tests.unit', '--cov-append', + '--cov-config=.coveragerc', '--cov-report=', '--cov-fail-under=97', + 'tests/unit', + ) + + +@nox.session +@nox.parametrize('python_version', ['2.7', '3.6']) +def system_tests(session, python_version): + """Run the system test suite.""" + + # Sanity check: Only run system tests if the environment variable is set. + if not os.environ.get('GOOGLE_APPLICATION_CREDENTIALS', ''): + return + + # Run the system tests against latest Python 2 and Python 3 only. + session.interpreter = 'python%s' % python_version + + # Install all test dependencies, then install this package into the + # virutalenv's dist-packages. + session.install('mock', 'pytest', + '../core/', '../test_utils/') + session.install('.') + + # Run py.test against the system tests. + session.run('py.test', '--quiet', 'tests/system.py') + + +@nox.session +def lint(session): + """Run flake8. + + Returns a failure if flake8 finds linting errors or sufficiently + serious code quality issues. + """ + session.interpreter = 'python3.6' + session.install('flake8') + session.install('.') + session.run('flake8', 'google/cloud/spanner') + + +@nox.session +def cover(session): + """Run the final coverage report. + + This outputs the coverage report aggregating coverage from the unit + test runs (not system test runs), and then erases coverage data. + """ + session.interpreter = 'python3.6' + session.install('coverage', 'pytest-cov') + session.run('coverage', 'report', '--show-missing', '--fail-under=100') + session.run('coverage', 'erase') diff --git a/spanner/setup.py b/spanner/setup.py index bf0a989556e4..cf59f658dd6d 100644 --- a/spanner/setup.py +++ b/spanner/setup.py @@ -66,7 +66,7 @@ 'google', 'google.cloud', ], - packages=find_packages(), + packages=find_packages(exclude=('unit_tests*',)), install_requires=REQUIREMENTS, **SETUP_BASE ) diff --git a/spanner/tests/__init__.py b/spanner/tests/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/system_tests/spanner.py b/spanner/tests/system.py similarity index 97% rename from system_tests/spanner.py rename to spanner/tests/system.py index 47d694d717ab..cddfa937e97c 100644 --- a/system_tests/spanner.py +++ b/spanner/tests/system.py @@ -22,13 +22,13 @@ from google.cloud.spanner.pool import BurstyPool from google.cloud.spanner._fixtures import DDL_STATEMENTS -from retry import RetryErrors -from retry import RetryInstanceState -from retry import RetryResult -from system_test_utils import unique_resource_id +from test_utils.retry import RetryErrors +from test_utils.retry import RetryInstanceState +from test_utils.retry import RetryResult +from test_utils.system import unique_resource_id -IS_TRAVIS = os.getenv('TRAVIS') == 'true' -CREATE_INSTANCE = IS_TRAVIS or os.getenv( +IS_CIRCLE = os.getenv('CIRCLECI') == 'true' +CREATE_INSTANCE = IS_CIRCLE or os.getenv( 'GOOGLE_CLOUD_TESTS_CREATE_SPANNER_INSTANCE') is not None if CREATE_INSTANCE: @@ -216,12 +216,12 @@ def test_update_database_ddl(self): self.to_delete.append(temp_db) # We want to make sure the operation completes. - create_op.result(60) # raises on failure / timeout. + create_op.result(90) # raises on failure / timeout. operation = temp_db.update_ddl(DDL_STATEMENTS) # We want to make sure the operation completes. - operation.result(30) # raises on failure / timeout. + operation.result(90) # raises on failure / timeout. temp_db.reload() diff --git a/spanner/unit_tests/__init__.py b/spanner/tests/unit/__init__.py similarity index 100% rename from spanner/unit_tests/__init__.py rename to spanner/tests/unit/__init__.py diff --git a/spanner/unit_tests/streaming-read-acceptance-test.json b/spanner/tests/unit/streaming-read-acceptance-test.json similarity index 100% rename from spanner/unit_tests/streaming-read-acceptance-test.json rename to spanner/tests/unit/streaming-read-acceptance-test.json diff --git a/spanner/unit_tests/test__helpers.py b/spanner/tests/unit/test__helpers.py similarity index 100% rename from spanner/unit_tests/test__helpers.py rename to spanner/tests/unit/test__helpers.py diff --git a/spanner/unit_tests/test_batch.py b/spanner/tests/unit/test_batch.py similarity index 100% rename from spanner/unit_tests/test_batch.py rename to spanner/tests/unit/test_batch.py diff --git a/spanner/unit_tests/test_client.py b/spanner/tests/unit/test_client.py similarity index 100% rename from spanner/unit_tests/test_client.py rename to spanner/tests/unit/test_client.py diff --git a/spanner/unit_tests/test_database.py b/spanner/tests/unit/test_database.py similarity index 100% rename from spanner/unit_tests/test_database.py rename to spanner/tests/unit/test_database.py diff --git a/spanner/unit_tests/test_instance.py b/spanner/tests/unit/test_instance.py similarity index 100% rename from spanner/unit_tests/test_instance.py rename to spanner/tests/unit/test_instance.py diff --git a/spanner/unit_tests/test_keyset.py b/spanner/tests/unit/test_keyset.py similarity index 100% rename from spanner/unit_tests/test_keyset.py rename to spanner/tests/unit/test_keyset.py diff --git a/spanner/unit_tests/test_pool.py b/spanner/tests/unit/test_pool.py similarity index 100% rename from spanner/unit_tests/test_pool.py rename to spanner/tests/unit/test_pool.py diff --git a/spanner/unit_tests/test_session.py b/spanner/tests/unit/test_session.py similarity index 100% rename from spanner/unit_tests/test_session.py rename to spanner/tests/unit/test_session.py diff --git a/spanner/unit_tests/test_snapshot.py b/spanner/tests/unit/test_snapshot.py similarity index 100% rename from spanner/unit_tests/test_snapshot.py rename to spanner/tests/unit/test_snapshot.py diff --git a/spanner/unit_tests/test_streamed.py b/spanner/tests/unit/test_streamed.py similarity index 100% rename from spanner/unit_tests/test_streamed.py rename to spanner/tests/unit/test_streamed.py diff --git a/spanner/unit_tests/test_transaction.py b/spanner/tests/unit/test_transaction.py similarity index 100% rename from spanner/unit_tests/test_transaction.py rename to spanner/tests/unit/test_transaction.py diff --git a/spanner/tox.ini b/spanner/tox.ini deleted file mode 100644 index 9e509cc9b05e..000000000000 --- a/spanner/tox.ini +++ /dev/null @@ -1,31 +0,0 @@ -[tox] -envlist = - py27,py34,py35,cover - -[testing] -deps = - {toxinidir}/../core - pytest - mock -covercmd = - py.test --quiet \ - --cov=google.cloud.spanner \ - --cov=unit_tests \ - --cov-config {toxinidir}/.coveragerc \ - unit_tests - -[testenv] -commands = - py.test --quiet {posargs} unit_tests -deps = - {[testing]deps} - -[testenv:cover] -basepython = - python2.7 -commands = - {[testing]covercmd} -deps = - {[testenv]deps} - coverage - pytest-cov diff --git a/speech/.flake8 b/speech/.flake8 new file mode 100644 index 000000000000..25168dc87605 --- /dev/null +++ b/speech/.flake8 @@ -0,0 +1,6 @@ +[flake8] +exclude = + __pycache__, + .git, + *.pyc, + conf.py diff --git a/speech/LICENSE b/speech/LICENSE new file mode 100644 index 000000000000..d64569567334 --- /dev/null +++ b/speech/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed 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. diff --git a/speech/MANIFEST.in b/speech/MANIFEST.in index cb3a2b9ef4fa..9f7100c9528a 100644 --- a/speech/MANIFEST.in +++ b/speech/MANIFEST.in @@ -1,4 +1,4 @@ -include README.rst -graft google -graft unit_tests -global-exclude *.pyc +include README.rst LICENSE +recursive-include google *.json *.proto +recursive-include unit_tests * +global-exclude *.pyc __pycache__ diff --git a/speech/google/cloud/speech/__init__.py b/speech/google/cloud/speech/__init__.py index 67b9aaf512fa..9c1654a2a6c7 100644 --- a/speech/google/cloud/speech/__init__.py +++ b/speech/google/cloud/speech/__init__.py @@ -22,3 +22,6 @@ from google.cloud.speech.client import Client from google.cloud.speech.encoding import Encoding from google.cloud.speech.operation import Operation + + +__all__ = ['__version__', 'Alternative', 'Client', 'Encoding', 'Operation'] diff --git a/speech/nox.py b/speech/nox.py new file mode 100644 index 000000000000..789afa3e37c4 --- /dev/null +++ b/speech/nox.py @@ -0,0 +1,89 @@ +# Copyright 2016 Google Inc. +# +# Licensed 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. + +from __future__ import absolute_import + +import os + +import nox + + +@nox.session +@nox.parametrize('python_version', ['2.7', '3.4', '3.5', '3.6']) +def unit_tests(session, python_version): + """Run the unit test suite.""" + + # Run unit tests against all supported versions of Python. + session.interpreter = 'python%s' % python_version + + # Install all test dependencies, then install this package in-place. + session.install('mock', 'pytest', 'pytest-cov', '../core/') + session.install('-e', '.') + + # Run py.test against the unit tests. + session.run('py.test', '--quiet', + '--cov=google.cloud.speech', '--cov=tests.unit', '--cov-append', + '--cov-config=.coveragerc', '--cov-report=', '--cov-fail-under=97', + 'tests/unit', + ) + + +@nox.session +@nox.parametrize('python_version', ['2.7', '3.6']) +def system_tests(session, python_version): + """Run the system test suite.""" + + # Sanity check: Only run system tests if the environment variable is set. + if not os.environ.get('GOOGLE_APPLICATION_CREDENTIALS', ''): + return + + # Run the system tests against latest Python 2 and Python 3 only. + session.interpreter = 'python%s' % python_version + + # Install all test dependencies, then install this package into the + # virutalenv's dist-packages. + session.install('mock', 'pytest', + '../core/', '../test_utils/', + '../storage/') + session.install('.') + + # Run py.test against the system tests. + session.run('py.test', '--quiet', 'tests/system.py') + + +@nox.session +def lint(session): + """Run flake8. + + Returns a failure if flake8 finds linting errors or sufficiently + serious code quality issues. + """ + session.interpreter = 'python3.6' + session.install('flake8') + session.install('.') + session.run('flake8', 'google/cloud/speech') + + +@nox.session +def cover(session): + """Run the final coverage report. + + This outputs the coverage report aggregating coverage from the unit + test runs (not system test runs), and then erases coverage data. + """ + session.interpreter = 'python3.6' + session.chdir(os.path.dirname(__file__)) + session.install('coverage', 'pytest-cov') + session.run('coverage', 'report', '--show-missing', '--fail-under=100') + session.run('coverage', 'erase') diff --git a/speech/setup.py b/speech/setup.py index c1c729e592ed..dd7fc501c939 100644 --- a/speech/setup.py +++ b/speech/setup.py @@ -63,7 +63,7 @@ 'google', 'google.cloud', ], - packages=find_packages(), + packages=find_packages(exclude=('unit_tests*',)), install_requires=REQUIREMENTS, **SETUP_BASE ) diff --git a/speech/tests/__init__.py b/speech/tests/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/system_tests/data/hello.wav b/speech/tests/data/hello.wav similarity index 100% rename from system_tests/data/hello.wav rename to speech/tests/data/hello.wav diff --git a/system_tests/speech.py b/speech/tests/system.py similarity index 98% rename from system_tests/speech.py rename to speech/tests/system.py index 14e80b7cfc1d..d900240de014 100644 --- a/system_tests/speech.py +++ b/speech/tests/system.py @@ -20,9 +20,9 @@ from google.cloud import storage from google.cloud.speech.alternative import Alternative -from system_test_utils import unique_resource_id -from retry import RetryErrors -from retry import RetryResult +from test_utils.retry import RetryErrors +from test_utils.retry import RetryResult +from test_utils.system import unique_resource_id AUDIO_FILE = os.path.join(os.path.dirname(__file__), 'data', 'hello.wav') @@ -33,7 +33,7 @@ def _operation_complete(result): return result -def _wait_until_complete(operation, max_attempts=5): +def _wait_until_complete(operation, max_attempts=10): """Wait until an operation has completed. :type operation: :class:`google.cloud.operation.Operation` diff --git a/speech/unit_tests/__init__.py b/speech/tests/unit/__init__.py similarity index 100% rename from speech/unit_tests/__init__.py rename to speech/tests/unit/__init__.py diff --git a/speech/unit_tests/_fixtures.py b/speech/tests/unit/_fixtures.py similarity index 100% rename from speech/unit_tests/_fixtures.py rename to speech/tests/unit/_fixtures.py diff --git a/speech/unit_tests/test__gax.py b/speech/tests/unit/test__gax.py similarity index 100% rename from speech/unit_tests/test__gax.py rename to speech/tests/unit/test__gax.py diff --git a/speech/unit_tests/test__http.py b/speech/tests/unit/test__http.py similarity index 100% rename from speech/unit_tests/test__http.py rename to speech/tests/unit/test__http.py diff --git a/speech/unit_tests/test_alternative.py b/speech/tests/unit/test_alternative.py similarity index 100% rename from speech/unit_tests/test_alternative.py rename to speech/tests/unit/test_alternative.py diff --git a/speech/unit_tests/test_client.py b/speech/tests/unit/test_client.py similarity index 99% rename from speech/unit_tests/test_client.py rename to speech/tests/unit/test_client.py index 8aa83daeecf0..89ba2c401609 100644 --- a/speech/unit_tests/test_client.py +++ b/speech/tests/unit/test_client.py @@ -127,7 +127,7 @@ def test_sync_recognize_content_with_optional_params_no_gax(self): from google.cloud import speech from google.cloud.speech.alternative import Alternative from google.cloud.speech.result import Result - from unit_tests._fixtures import SYNC_RECOGNIZE_RESPONSE + from tests.unit._fixtures import SYNC_RECOGNIZE_RESPONSE _b64_audio_content = _bytes_to_unicode(b64encode(self.AUDIO_CONTENT)) request = { @@ -184,7 +184,7 @@ def test_sync_recognize_source_uri_without_optional_params_no_gax(self): from google.cloud import speech from google.cloud.speech.alternative import Alternative from google.cloud.speech.result import Result - from unit_tests._fixtures import SYNC_RECOGNIZE_RESPONSE + from tests.unit._fixtures import SYNC_RECOGNIZE_RESPONSE request = { 'config': { @@ -230,7 +230,7 @@ def test_sync_recognize_source_uri_without_optional_params_no_gax(self): def test_sync_recognize_with_empty_results_no_gax(self): from google.cloud import speech - from unit_tests._fixtures import SYNC_RECOGNIZE_EMPTY_RESPONSE + from tests.unit._fixtures import SYNC_RECOGNIZE_EMPTY_RESPONSE credentials = _make_credentials() client = self._make_one(credentials=credentials, use_gax=False) @@ -365,7 +365,7 @@ def test_async_supported_encodings(self): def test_async_recognize_no_gax(self): from google.cloud import speech from google.cloud.speech.operation import Operation - from unit_tests._fixtures import ASYNC_RECOGNIZE_RESPONSE + from tests.unit._fixtures import ASYNC_RECOGNIZE_RESPONSE RETURNED = ASYNC_RECOGNIZE_RESPONSE diff --git a/speech/unit_tests/test_operation.py b/speech/tests/unit/test_operation.py similarity index 100% rename from speech/unit_tests/test_operation.py rename to speech/tests/unit/test_operation.py diff --git a/speech/unit_tests/test_result.py b/speech/tests/unit/test_result.py similarity index 100% rename from speech/unit_tests/test_result.py rename to speech/tests/unit/test_result.py diff --git a/speech/unit_tests/test_sample.py b/speech/tests/unit/test_sample.py similarity index 100% rename from speech/unit_tests/test_sample.py rename to speech/tests/unit/test_sample.py diff --git a/speech/tox.ini b/speech/tox.ini deleted file mode 100644 index 4ded9cd86434..000000000000 --- a/speech/tox.ini +++ /dev/null @@ -1,35 +0,0 @@ -[tox] -envlist = - py27,py34,py35,cover - -[testing] -localdeps = - pip install --quiet --upgrade {toxinidir}/../core -deps = - {toxinidir}/../core - mock - pytest -covercmd = - py.test --quiet \ - --cov=google.cloud.speech \ - --cov=unit_tests \ - --cov-config {toxinidir}/.coveragerc \ - unit_tests - -[testenv] -commands = - {[testing]localdeps} - py.test --quiet {posargs} unit_tests -deps = - {[testing]deps} - -[testenv:cover] -basepython = - python2.7 -commands = - {[testing]localdeps} - {[testing]covercmd} -deps = - {[testenv]deps} - coverage - pytest-cov diff --git a/storage/.flake8 b/storage/.flake8 new file mode 100644 index 000000000000..25168dc87605 --- /dev/null +++ b/storage/.flake8 @@ -0,0 +1,6 @@ +[flake8] +exclude = + __pycache__, + .git, + *.pyc, + conf.py diff --git a/storage/LICENSE b/storage/LICENSE new file mode 100644 index 000000000000..d64569567334 --- /dev/null +++ b/storage/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed 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. diff --git a/storage/MANIFEST.in b/storage/MANIFEST.in index cb3a2b9ef4fa..9f7100c9528a 100644 --- a/storage/MANIFEST.in +++ b/storage/MANIFEST.in @@ -1,4 +1,4 @@ -include README.rst -graft google -graft unit_tests -global-exclude *.pyc +include README.rst LICENSE +recursive-include google *.json *.proto +recursive-include unit_tests * +global-exclude *.pyc __pycache__ diff --git a/storage/google/cloud/storage/__init__.py b/storage/google/cloud/storage/__init__.py index 3303be5bdd7e..433f711025f6 100644 --- a/storage/google/cloud/storage/__init__.py +++ b/storage/google/cloud/storage/__init__.py @@ -38,3 +38,6 @@ from google.cloud.storage.blob import Blob from google.cloud.storage.bucket import Bucket from google.cloud.storage.client import Client + + +__all__ = ['__version__', 'Batch', 'Blob', 'Bucket', 'Client'] diff --git a/storage/nox.py b/storage/nox.py new file mode 100644 index 000000000000..7c7ff713df73 --- /dev/null +++ b/storage/nox.py @@ -0,0 +1,87 @@ +# Copyright 2016 Google Inc. +# +# Licensed 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. + +from __future__ import absolute_import + +import os + +import nox + + +@nox.session +@nox.parametrize('python_version', ['2.7', '3.4', '3.5', '3.6']) +def unit_tests(session, python_version): + """Run the unit test suite.""" + + # Run unit tests against all supported versions of Python. + session.interpreter = 'python%s' % python_version + + # Install all test dependencies, then install this package in-place. + session.install('mock', 'pytest', 'pytest-cov', '../core/') + session.install('-e', '.') + + # Run py.test against the unit tests. + session.run('py.test', '--quiet', + '--cov=google.cloud.storage', '--cov=tests.unit', '--cov-append', + '--cov-config=.coveragerc', '--cov-report=', '--cov-fail-under=97', + 'tests/unit', + ) + + +@nox.session +@nox.parametrize('python_version', ['2.7', '3.6']) +def system_tests(session, python_version): + """Run the system test suite.""" + + # Sanity check: Only run system tests if the environment variable is set. + if not os.environ.get('GOOGLE_APPLICATION_CREDENTIALS', ''): + return + + # Run the system tests against latest Python 2 and Python 3 only. + session.interpreter = 'python%s' % python_version + + # Install all test dependencies, then install this package into the + # virutalenv's dist-packages. + session.install('mock', 'pytest', + '../core/', '../test_utils/') + session.install('.') + + # Run py.test against the system tests. + session.run('py.test', '--quiet', 'tests/system.py') + + +@nox.session +def lint(session): + """Run flake8. + + Returns a failure if flake8 finds linting errors or sufficiently + serious code quality issues. + """ + session.interpreter = 'python3.6' + session.install('flake8') + session.install('.') + session.run('flake8', 'google/cloud/storage') + + +@nox.session +def cover(session): + """Run the final coverage report. + + This outputs the coverage report aggregating coverage from the unit + test runs (not system test runs), and then erases coverage data. + """ + session.interpreter = 'python3.6' + session.install('coverage', 'pytest-cov') + session.run('coverage', 'report', '--show-missing', '--fail-under=100') + session.run('coverage', 'erase') diff --git a/storage/setup.py b/storage/setup.py index 984c5edb3f7b..f44f9dcf988c 100644 --- a/storage/setup.py +++ b/storage/setup.py @@ -62,7 +62,7 @@ 'google', 'google.cloud', ], - packages=find_packages(), + packages=find_packages(exclude=('unit_tests*',)), install_requires=REQUIREMENTS, **SETUP_BASE ) diff --git a/storage/tests/__init__.py b/storage/tests/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/system_tests/data/CloudPlatform_128px_Retina.png b/storage/tests/data/CloudPlatform_128px_Retina.png similarity index 100% rename from system_tests/data/CloudPlatform_128px_Retina.png rename to storage/tests/data/CloudPlatform_128px_Retina.png diff --git a/system_tests/data/five-point-one-mb-file.zip b/storage/tests/data/five-point-one-mb-file.zip similarity index 100% rename from system_tests/data/five-point-one-mb-file.zip rename to storage/tests/data/five-point-one-mb-file.zip diff --git a/system_tests/data/simple.txt b/storage/tests/data/simple.txt similarity index 100% rename from system_tests/data/simple.txt rename to storage/tests/data/simple.txt diff --git a/system_tests/storage.py b/storage/tests/system.py similarity index 98% rename from system_tests/storage.py rename to storage/tests/system.py index d5ca64f007f7..e4b64bd88304 100644 --- a/system_tests/storage.py +++ b/storage/tests/system.py @@ -24,8 +24,8 @@ from google.cloud import storage from google.cloud.storage._helpers import _base64_md5hash -from system_test_utils import unique_resource_id -from retry import RetryErrors +from test_utils.retry import RetryErrors +from test_utils.system import unique_resource_id HTTP = httplib2.Http() @@ -120,15 +120,16 @@ def test_list_buckets(self): class TestStorageFiles(unittest.TestCase): + DIRNAME = os.path.realpath(os.path.dirname(__file__)) FILES = { 'logo': { - 'path': 'system_tests/data/CloudPlatform_128px_Retina.png', + 'path': DIRNAME + '/data/CloudPlatform_128px_Retina.png', }, 'big': { - 'path': 'system_tests/data/five-point-one-mb-file.zip', + 'path': DIRNAME + '/data/five-point-one-mb-file.zip', }, 'simple': { - 'path': 'system_tests/data/simple.txt', + 'path': DIRNAME + '/data/simple.txt', } } diff --git a/storage/unit_tests/__init__.py b/storage/tests/unit/__init__.py similarity index 100% rename from storage/unit_tests/__init__.py rename to storage/tests/unit/__init__.py diff --git a/storage/unit_tests/test__helpers.py b/storage/tests/unit/test__helpers.py similarity index 100% rename from storage/unit_tests/test__helpers.py rename to storage/tests/unit/test__helpers.py diff --git a/storage/unit_tests/test__http.py b/storage/tests/unit/test__http.py similarity index 100% rename from storage/unit_tests/test__http.py rename to storage/tests/unit/test__http.py diff --git a/storage/unit_tests/test_acl.py b/storage/tests/unit/test_acl.py similarity index 100% rename from storage/unit_tests/test_acl.py rename to storage/tests/unit/test_acl.py diff --git a/storage/unit_tests/test_batch.py b/storage/tests/unit/test_batch.py similarity index 100% rename from storage/unit_tests/test_batch.py rename to storage/tests/unit/test_batch.py diff --git a/storage/unit_tests/test_blob.py b/storage/tests/unit/test_blob.py similarity index 100% rename from storage/unit_tests/test_blob.py rename to storage/tests/unit/test_blob.py diff --git a/storage/unit_tests/test_bucket.py b/storage/tests/unit/test_bucket.py similarity index 100% rename from storage/unit_tests/test_bucket.py rename to storage/tests/unit/test_bucket.py diff --git a/storage/unit_tests/test_client.py b/storage/tests/unit/test_client.py similarity index 100% rename from storage/unit_tests/test_client.py rename to storage/tests/unit/test_client.py diff --git a/storage/tox.ini b/storage/tox.ini deleted file mode 100644 index 21a37c145f0e..000000000000 --- a/storage/tox.ini +++ /dev/null @@ -1,35 +0,0 @@ -[tox] -envlist = - py27,py34,py35,cover - -[testing] -localdeps = - pip install --quiet --upgrade {toxinidir}/../core -deps = - {toxinidir}/../core - mock - pytest -covercmd = - py.test --quiet \ - --cov=google.cloud.storage \ - --cov=unit_tests \ - --cov-config {toxinidir}/.coveragerc \ - unit_tests - -[testenv] -commands = - {[testing]localdeps} - py.test --quiet {posargs} unit_tests -deps = - {[testing]deps} - -[testenv:cover] -basepython = - python2.7 -commands = - {[testing]localdeps} - {[testing]covercmd} -deps = - {[testenv]deps} - coverage - pytest-cov diff --git a/system_tests/app_credentials.json.sample b/system_tests/app_credentials.json.sample deleted file mode 100644 index b37488e19d3b..000000000000 --- a/system_tests/app_credentials.json.sample +++ /dev/null @@ -1,7 +0,0 @@ -{ - "client_id": "my-project", - "client_email": "some-account@developer.gserviceaccount.com", - "private_key": "-----BEGIN PRIVATE KEY-----\nSome Private Key Text\n-----END PRIVATE KEY-----\n", - "private_key_id": "your-key-id", - "type": "service_account" -} diff --git a/system_tests/attempt_system_tests.py b/system_tests/attempt_system_tests.py deleted file mode 100644 index 747d70b62a34..000000000000 --- a/system_tests/attempt_system_tests.py +++ /dev/null @@ -1,194 +0,0 @@ -# Copyright 2016 Google Inc. -# -# Licensed 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. - -"""Attempt to run system tests. - -If the system tests are being run on Travis, no test need be run if -the build is for a PR and not a merged commit. - -If being run as part of a Travis build for a merged commit, the -encrypted `key.json` file need be decrypted before running tests. - -Before encrypting: - -* Visit ``https://github.com/settings/tokens/new``, get a token, - and store the token in travis.token file. -* Visit - ``https://console.cloud.google.com/apis/credentials?project={PROJ}`` -* Click "Create credentials >> Service account key", select your - service account and create a JSON key (we'll call it ``key.json``) - -Encrypt the file via:: - -$ travis login --github-token=$(cat travis.token) -$ travis encrypt-file --repo=GoogleCloudPlatform/google-cloud-python key.json - -After running ``travis encrypt-file``, take note of the ``openssl`` command -printed out. In particular, it'll give the name of the key and -IV (initialization vector) environment variables added to the Travis -project. - -See: -https://docs.travis-ci.com/user/encrypting-files/ -""" - - -from __future__ import print_function -import argparse -import os -import subprocess -import sys - -from google.auth.environment_vars import CREDENTIALS - -from run_system_test import FailedSystemTestModule -from run_system_test import run_module_tests - - -MODULES = ( # ordered from most to least stable - 'datastore', - 'storage', - 'speech', - 'vision', - 'bigquery', - 'pubsub', - 'language', - 'logging', - 'translate', - 'monitoring', - 'bigtable', - 'spanner', -) - -SCRIPTS_DIR = os.path.dirname(__file__) -ROOT_DIR = os.path.abspath(os.path.join(SCRIPTS_DIR, '..')) -ENCRYPTED_KEYFILE = os.path.join(ROOT_DIR, 'system_tests', 'key.json.enc') -ENCRYPTED_KEY_ENV = 'encrypted_c16407eb06cc_key' -ENCRYPTED_INIT_VECTOR_ENV = 'encrypted_c16407eb06cc_iv' -ALL_MODULES = object() # Sentinel for argparser - - -def check_environment(): - """Check what environment this is running in. - - In particular, if the environment is Travis. - - :rtype: tuple - :returns: A pair of booleans. The first indicates if the test - is running in Travis and the second indicates if - the current build is a non-PR for a merge to master. - """ - if os.getenv('TRAVIS') == 'true': - is_travis = True - non_pr = (os.getenv('TRAVIS_PULL_REQUEST') == 'false' and - os.getenv('TRAVIS_BRANCH') == 'master') - else: - is_travis = non_pr = False - - return is_travis, non_pr - - -def decrypt_keyfile(): - """Decrypt a keyfile.""" - print('Running in Travis during merge, decrypting stored ' - 'key file.') - - encrypted_key = os.getenv(ENCRYPTED_KEY_ENV) - encrypted_iv = os.getenv(ENCRYPTED_INIT_VECTOR_ENV) - out_file = os.getenv(CREDENTIALS) - # Convert encrypted key file into decrypted file to be used. - subprocess.call([ - 'openssl', 'aes-256-cbc', - '-K', encrypted_key, - '-iv', encrypted_iv, - '-in', ENCRYPTED_KEYFILE, - '-out', out_file, '-d' - ]) - - -def prepare_to_run(): - """Prepare to run system tests. - - If on Travis during a PR, exit the entire program; there is - no need to run the system tests. - - If on Travis during a build for a non-PR merge to master, - decrypts stored keyfile. - """ - is_travis, non_pr = check_environment() - # Nothing to prepare outside of Travis. Proceed to tests. - if not is_travis: - return - - # On a Travis PR, exit the program. - if not non_pr: - print('Running in Travis during non-merge to master, ' - 'doing nothing.') - sys.exit(0) - - # On a Travis build for a merge commit to master, decrypt. - decrypt_keyfile() - - -def get_parser(): - """Get an argument parser to determine a list of packages.""" - parser = argparse.ArgumentParser( - description='google-cloud tests runner.') - help_msg = ('List of packages to be tested. ' - 'If left blank, tests all packages.') - parser.add_argument('packages', nargs='*', - default=ALL_MODULES, help=help_msg) - return parser - - -def get_modules(): - """Get the list of modules names to run system tests for.""" - parser = get_parser() - args = parser.parse_args() - if args.packages is ALL_MODULES: - result = list(MODULES) - else: - result = [] - invalid = [] - for package in args.packages: - if package in MODULES: - result.append(package) - else: - invalid.append(package) - - if invalid: - msg = 'No system test for packages: ' + ', '.join(invalid) - print(msg, file=sys.stderr) - sys.exit(1) - - return result - - -def main(): - """Run all the system tests if necessary.""" - prepare_to_run() - - failed_modules = 0 - modules = get_modules() - for module in modules: - try: - run_module_tests(module) - except FailedSystemTestModule: - failed_modules += 1 - - sys.exit(failed_modules) - - -if __name__ == '__main__': - main() diff --git a/system_tests/data/index.yaml b/system_tests/data/index.yaml deleted file mode 100644 index 5a2d2b1a8bc9..000000000000 --- a/system_tests/data/index.yaml +++ /dev/null @@ -1,23 +0,0 @@ -indexes: - -- kind: Character - ancestor: yes - properties: - - name: appearances - -- kind: Character - ancestor: yes - properties: - - name: alive - -- kind: Character - ancestor: yes - properties: - - name: family - - name: appearances - -- kind: Character - ancestor: yes - properties: - - name: name - - name: family diff --git a/system_tests/key.json.enc b/system_tests/key.json.enc deleted file mode 100644 index 1444a17dcd1c..000000000000 Binary files a/system_tests/key.json.enc and /dev/null differ diff --git a/system_tests/local_test_setup.sample b/system_tests/local_test_setup.sample deleted file mode 100644 index 63eb733a7c85..000000000000 --- a/system_tests/local_test_setup.sample +++ /dev/null @@ -1,3 +0,0 @@ -export GOOGLE_APPLICATION_CREDENTIALS="app_credentials.json.sample" -export GOOGLE_CLOUD_TESTING_REMOTE="upstream" -export GOOGLE_CLOUD_TESTING_BRANCH="master" diff --git a/system_tests/run_system_test.py b/system_tests/run_system_test.py deleted file mode 100644 index 2ea9999e9e56..000000000000 --- a/system_tests/run_system_test.py +++ /dev/null @@ -1,95 +0,0 @@ -# Copyright 2014 Google Inc. -# -# Licensed 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. - -import argparse -import sys -import unittest - -import bigquery -import bigtable -import datastore -import language -import logging_ -import monitoring -import pubsub -import spanner -import speech -import storage -import system_test_utils -import translate -import vision - - -TEST_MODULES = { - 'bigquery': bigquery, - 'bigtable': bigtable, - 'datastore': datastore, - 'language': language, - 'logging': logging_, - 'monitoring': monitoring, - 'pubsub': pubsub, - 'spanner': spanner, - 'speech': speech, - 'storage': storage, - 'translate': translate, - 'vision': vision, -} - - -class FailedSystemTestModule(Exception): - pass - - -def get_parser(): - parser = argparse.ArgumentParser( - description='google-cloud test runner against actual project.') - parser.add_argument('--package', dest='package', - choices=sorted(TEST_MODULES), - default='datastore', help='Package to be tested.') - parser.add_argument( - '--ignore-requirements', - dest='ignore_requirements', action='store_true', - help='Ignore the credentials requirement for the test.') - return parser - - -def run_module_tests(module_name, ignore_requirements=False): - if not ignore_requirements: - # Make sure environ is set before running test. - system_test_utils.check_environ() - - suite = unittest.TestSuite() - test_mod = TEST_MODULES[module_name] - tests = unittest.defaultTestLoader.loadTestsFromModule(test_mod) - suite.addTest(tests) - - # Run tests. - test_result = unittest.TextTestRunner(verbosity=2).run(suite) - # Exit if not successful. - if not test_result.wasSuccessful(): - raise FailedSystemTestModule(module_name) - - -def main(): - parser = get_parser() - args = parser.parse_args() - try: - run_module_tests(args.package, - ignore_requirements=args.ignore_requirements) - except FailedSystemTestModule: - sys.exit(1) - - -if __name__ == '__main__': - main() diff --git a/test_utils/credentials.json.enc b/test_utils/credentials.json.enc new file mode 100644 index 000000000000..f073c7e4f774 --- /dev/null +++ b/test_utils/credentials.json.enc @@ -0,0 +1,49 @@ +U2FsdGVkX1/vVm/dOEg1DCACYbdOcL+ey6+64A+DZGZVgF8Z/3skK6rpPocu6GOA +UZAqASsBH9QifDf8cKVXQXVYpYq6HSv2O0w7vOmVorZO9GYPo98s9/8XO+4ty/AU +aB6TD68frBAYv4cT/l5m7aYdzfzMTy0EOXoleZT09JYP3B5FV3KCO114FzMXGwrj +HXsR6E5SyUUlUnWPC3eD3aqmovay0gxOKYO3ZwjFK1nlbN/8q6/8nwBCf/Bg6SHV +V93pNxdolRlJev9kgKz4RN1z4jGCy5PAndhSLE82NFIs9LoAiEOU5YeMlN+Ulqus +J92nh+ptUe9a4pJGbAuveUWO7zdS1QyXvTMUcmmSfXCNm/eIQjNuu5+rHtIjWKh8 +Ilwj2w1aTfSptQEhk/kwRgFz/d11vfwJzvwTmCxO6zyOeL0VUWLqdCBGgG5As9He +/RenF8PZ1O0WbTt7fns5oTlTk/MUo+0xJ1xqvu/y45LaqqcBAnEdrWKmtM3dJHWv +ufQku+kD+83F/VwBnQdvgMHu6KZEs6LRrNo58r4QuK6fS7VCACdzxID1RM2cL7kT +6BFRlyGj1aigmjne9g9M9Jx4R+mZDpPU1WDzzG71J4qCUwaX8Dfwutuv4uiFvzwq +NUF0wLJJPtKWmtW+hnZ/fhHQGCRsOpZzFnqp6Zv7J7k6esqxMgIjfal7Djk5Acy8 +j3iVvm6CYmKMVqzL62JHYS9Ye83tzBCaR8hpnJQKgH3FSOFY8HSwrtQSIsl/hSeF +41sgnz0Y+/gkzNeU18qFk+eCZmvljyu+JK0nPYUgpOCJYVBNQpNHz5PUyiAEKhtM +IOSdjPRW1Y+Xf4RroJnLPoF24Ijwrow5LCm9hBRY6TPPMMmnIXCd23xcLJ1rMj6g +x4ZikElans+cwuc9wtbb7w01DcpTwQ1+eIV1qV+KIgpnLjRGLhZD4etobBsrwYu/ +vnIwy2QHCKENPb8sbdgp7x2mF7VSX0/7tf+9+i70EBiMzpOKBkiZhtLzm6hOBkEy +ODaWrx4lTTwbSw8Rmtf58APhPFMsjHoNsjiUoK249Y8Y2Ff4fMfqYsXu6VC1n/At +CuWYHc3EfBwFcLJS+RQB9kFk/4FygFBWq4Kj0MqoRruLbKmoGeJKH9q35W0f0NCD +j+iHt3014kMGiuyJe1UDQ6fvEihFFdHuDivFpPAXDt4PTY/WtpDhaGMx23kb54pK +jkAuxpznAB1lK3u9bGRXDasGeHIrNtIlPvgkrWHXvoBVqM7zry8TGtoxp3E3I42Z +cUfDWfB9GqVdrOwvrTzyZsl2uShRkAJaZFZj5aMyYxiptp4gM8CwWiNtOd2EwtRO +LxZX4M02PQFIqXV3FSDA0q6EwglUrTZdAlYeOEkopaKCtG31dEPOSQG3NGJAEYso +Cxm99H7970dp0OAgpNSgRbcWDbhVbQXnRzvFGqLeH6a9dQ/a8uD3s8Qm9Du/kB6d +XxTRe2OGxzcD0AgI8GClE4rIZHCLbcwuJRp0EYcN+pgY80O4U98fZ5RYpU6OYbU/ +MEiaBYFKtZtGkV6AQD568V7hHJWqc5DDfVHUQ/aeQwnKi2vnU66u+nnV2rZxXxLP ++dqeLRpul+wKa5b/Z5SfQ14Ff8s7aVyxaogGpyggyPL1vyq4KWZ6Or/wEE5hgNO4 +kBh6ht0QT1Hti8XY2JK1M+Jgbjgcg4jkHBGVqegrG1Rvcc2A4TYKwx+QMSBhyxrU +5qhROjS4lTcC42hQslMUkUwc4U/Y91XdFbOOnaAkwzI36NRYL0pmgZnYxGJZeRvr +E5foOhnOEVSFGdOkLfFh+FkWZQf56Lmn8Gg2wHE3dZTxLHibiUYfkgOr1uEosq29 +D1NstvlJURPQ0Q+8QQNWcl9nEZHMAjOmnL1hbx+QfuC6seucp+sXGzdZByMLZbvT +tG8KNL293CmyQowgf9MXToWYnwRkcvqfTaKyor2Ggze3JtoFW4t0j4DI1XPciZFX +XmfApHrzdB/bZadzxyaZ2NE0CuH9zDelwI6rz38xsN5liYnp5qmNKVCZVOHccXa6 +J8x365m5/VaaA2RrtdPqKxn8VaKy7+T690QgMXVGM4PbzQzQxHuSleklocqlP+sB +jSMXCZY+ng/i4UmRO9noiyW3UThYh0hIdMYs12EmmI9cnF/OuYZpl30fmqwV+VNM +td5B2fYvAvvsjiX60SFCn3DATP1GrPMBlZSmhhP3GYS+xrWt3Xxta9qIX2BEF1Gg +twnZZRjoULSRFUYPfJPEOfEH2UQwm84wxx/GezVE+S/RpBlatPOgCiLnNNaLfdTC +mTG9qY9elJv3GGQO8Lqgf4i8blExs05lSPk1BDhzTB6H9TLz+Ge0/l1QxKf3gPXU +aImK1azieXMXHECkdKxrzmehwu1dZ/oYOLc/OFQCETwSRoLPFOFpYUpizwmVVHR6 +uLSfRptte4ZOU3zHfpd/0+J4tkwHwEkGzsmMdqudlm7qME6upuIplyVBH8JiXzUK +n1RIH/OPmVEluAnexWRLZNdk7MrakIO4XACVbICENiYQgAIErP568An6twWEGDbZ +bEN64E3cVDTDRPRAunIhhsEaapcxpFEPWlHorxv36nMUt0R0h0bJlCu5QdzckfcX +ZrRuu1kl76ZfbSE8T0G4/rBb9gsU4Gn3WyvLIO3MgFBuxR68ZwcR8LpEUd8qp38H +NG4cxPmN1nGKo663Z+xI2Gt5up4gpl+fOt4mXqxY386rB7yHaOfElMG5TUYdrS9w +1xbbCVgeJ6zxX+NFlndG33cSAPprhw+C18eUu6ZU63WZcYFo3GfK6rs3lvYtofvE +8DxztdTidQedNVNE+63YCjhxd/cZUI5n/UpgYkr9owp7hNGJiR3tdoNLR2gcoGqL +qWhH928k2aSgF2j97LZ2OqoPCp0tUB7ho4jD2u4Ik3GLVNlCc3dCvWRvpHtDTQDv +tujESMfHUc9I2r4S/PD3bku/ABGwa977Yp1PjzJGr9RajA5is5n6GVpyynwjtKG4 +iyyITpdwpCgr8pueTBLwZnas3slmiMOog/E4PmPgctHzvC+vhQijhUtw5zSsmv0l +bZlw/mVhp5Ta7dTcLBKR8DA3m3vTbaEGkz0xpfQr7GfiSMRbJyvIw88pDK0gyTMD diff --git a/scripts/circleci_tagged_pkg.py b/test_utils/scripts/circleci/get_tagged_package.py similarity index 99% rename from scripts/circleci_tagged_pkg.py rename to test_utils/scripts/circleci/get_tagged_package.py index ba7c99f522d8..be90d34a4b40 100644 --- a/scripts/circleci_tagged_pkg.py +++ b/test_utils/scripts/circleci/get_tagged_package.py @@ -13,7 +13,6 @@ # limitations under the License. """Helper to determine package from tag. - Get the current package directory corresponding to the Circle Tag. """ @@ -40,7 +39,6 @@ def main(): """Get the current package directory. - Prints the package directory out so callers can consume it. """ if TAG_ENV not in os.environ: diff --git a/scripts/circleci_twine_upload.sh b/test_utils/scripts/circleci/twine_upload.sh similarity index 81% rename from scripts/circleci_twine_upload.sh rename to test_utils/scripts/circleci/twine_upload.sh index 42495226acf8..9bbf3b1b3121 100755 --- a/scripts/circleci_twine_upload.sh +++ b/test_utils/scripts/circleci/twine_upload.sh @@ -16,8 +16,14 @@ set -ev +# If this is not a CircleCI tag, no-op. +if [[ -z "$CIRCLE_TAG" ]]; then + echo "This is not a release tag. Doing nothing." + exit 0 +fi + # H/T: http://stackoverflow.com/a/246128/1068170 -SCRIPT="$(dirname "${BASH_SOURCE[0]}")/circleci_tagged_pkg.py" +SCRIPT="$(dirname "${BASH_SOURCE[0]}")/get_tagged_package.py" # Determine the package directory being deploying on this tag. PKG_DIR="$(python ${SCRIPT})" diff --git a/test_utils/scripts/get_target_packages.py b/test_utils/scripts/get_target_packages.py new file mode 100644 index 000000000000..f53f5ece5789 --- /dev/null +++ b/test_utils/scripts/get_target_packages.py @@ -0,0 +1,149 @@ +# Copyright 2017 Google Inc. +# +# Licensed 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. + +"""Print a list of packages which require testing.""" + +import os +import subprocess +import warnings + + +CURRENT_DIR = os.path.realpath(os.path.dirname(__file__)) +BASE_DIR = os.path.realpath(os.path.join(CURRENT_DIR, '..', '..')) +GITHUB_REPO = os.environ.get('GITHUB_REPO', 'google-cloud-python') + +# This is the current set of dependencies by package. +# As of this writing, the only "real" dependency is that of error_reporting +# (on logging), the rest are just system test dependencies. +PKG_DEPENDENCIES = { + 'bigquery': {'storage'}, + 'error_reporting': {'logging'}, + 'language': {'storage'}, + 'logging': {'bigquery', 'pubsub', 'storage'}, + 'speech': {'storage'}, + 'vision': {'storage'}, +} + + +def get_baseline(): + """Return the baseline commit. + + On a pull request, or on a branch, return the master tip. + + Locally, return a value pulled from environment variables, or None if + the environment variables are not set. + + On a push to master, return None. This will effectively cause everything + to be considered to be affected. + """ + # If this is a pull request or branch, return the tip for master. + # We will test only packages which have changed since that point. + ci_non_master = os.environ.get('CI', '') == 'true' and any([ + os.environ.get('CIRCLE_BRANCH', '') != 'master', + os.environ.get('CIRCLE_PR_NUMBER', ''), + ]) + if ci_non_master: + subprocess.run(['git', 'remote', 'add', 'baseline', + 'git@github.com:GoogleCloudPlatform/%s' % GITHUB_REPO], + stderr=subprocess.DEVNULL) + subprocess.run(['git', 'pull', 'baseline'], stderr=subprocess.DEVNULL) + return 'baseline/master' + + # If environment variables are set identifying what the master tip is, + # use that. + if os.environ.get('GOOGLE_CLOUD_TESTING_REMOTE', ''): + remote = os.environ['GOOGLE_CLOUD_TESTING_REMOTE'] + branch = os.environ.get('GOOGLE_CLOUD_TESTING_BRANCH', 'master') + return '%s/%s' % (remote, branch) + + # If we are not in CI and we got this far, issue a warning. + if not os.environ.get('CI', ''): + warnings.warn('No baseline could be determined; this means tests ' + 'will run for every package. If this is local ' + 'development, set the $GOOGLE_CLOUD_TESTING_REMOTE ' + 'environment variable.') + + # That is all we can do; return None. + return None + + +def get_changed_files(): + """Return a list of files that have been changed since the baseline. + + If there is no base, return None. + """ + # Get the baseline, and fail quickly if there is no baseline. + baseline = get_baseline() + if not baseline: + return None + + # Return a list of altered files. + try: + return subprocess.check_output([ + 'git', 'diff', '--name-only', '%s..HEAD' % baseline, + ], stderr=subprocess.DEVNULL).decode('utf8').strip().split('\n') + except subprocess.CalledProcessError: + warnings.warn('Unable to perform git diff; falling back to assuming ' + 'all packages have changed.') + return None + + +def get_changed_packages(file_list): + """Return a list of changed packages based on the provided file list. + + If the file list is None, then all packages should be considered to be + altered. + """ + # Determine a complete list of packages. + all_packages = set() + for file_ in os.listdir(BASE_DIR): + abs_file = os.path.realpath(os.path.join(BASE_DIR, file_)) + if os.path.isdir(abs_file) and os.path.isfile('%s/nox.py' % abs_file): + all_packages.add(file_) + + # If ther is no file list, send down the full package set. + if file_list is None: + return all_packages + + # Create a set based on the list of changed files. + answer = set() + for file_ in file_list: + # Ignore root directory changes (setup.py, .gitignore, etc.). + if '/' not in file_: + continue + + # Ignore changes that are not in a package (usually this will be docs). + package = file_.split('/')[0] + if package not in all_packages: + continue + + # If there is a change in core, short-circuit now and return + # everything. + if package == 'core': + return all_packages + + # Add the package, as well as any dependencies this package has. + # NOTE: For now, dependencies only go down one level. + answer.add(package) + answer = answer.union(PKG_DEPENDENCIES.get(package, set())) + + # We got this far without being short-circuited; return the final answer. + return answer + + +if __name__ == '__main__': + # Figure out what packages have changed. + file_list = get_changed_files() + for package in sorted(get_changed_packages(file_list)): + print(package) diff --git a/scripts/get_version.py b/test_utils/scripts/get_version.py similarity index 99% rename from scripts/get_version.py rename to test_utils/scripts/get_version.py index 514ce87adaf9..b56eb2de2f7f 100644 --- a/scripts/get_version.py +++ b/test_utils/scripts/get_version.py @@ -16,4 +16,6 @@ from __future__ import print_function from pkg_resources import get_distribution + + print(get_distribution('google-cloud').version) diff --git a/system_tests/run_emulator.py b/test_utils/scripts/run_emulator.py similarity index 100% rename from system_tests/run_emulator.py rename to test_utils/scripts/run_emulator.py diff --git a/scripts/update_docs.sh b/test_utils/scripts/update_docs.sh similarity index 66% rename from scripts/update_docs.sh rename to test_utils/scripts/update_docs.sh index 30e983ec87d6..16c3c1ae719f 100755 --- a/scripts/update_docs.sh +++ b/test_utils/scripts/update_docs.sh @@ -19,19 +19,23 @@ set -ev GH_OWNER="GoogleCloudPlatform" GH_PROJECT_NAME="google-cloud-python" -######################################### -# Only update docs if we are on Travis. # -######################################### -if [[ "${TRAVIS_BRANCH}" == "master" ]] && \ - [[ "${TRAVIS_PULL_REQUEST}" == "false" ]]; then - echo "Building new docs on a merged commit." -elif [[ -n "${TRAVIS_TAG}" ]]; then - echo "Building new docs on a tag." +# Function to build the docs. +function build_docs { + rm -rf docs/_build/ + sphinx-build -W -b html -d docs/_build/doctrees docs/ docs/_build/html/ + return $? +} + +# Only update docs if we are on CircleCI. +if [[ "${CIRCLE_BRANCH}" == "master" ]] && [[ -z "${CIRCLE_PR_NUMBER}" ]]; then + echo "Building new docs on a merged commit." +elif [[ -n "${CIRCLE_TAG}" ]]; then + echo "Building new docs on a tag." else - echo "No docs to update for a new tag or merged commit on Travis." - echo "Verifying docs build successfully." - tox -e docs - exit + echo "Not on master nor a release tag." + echo "Building new docs for testing purposes, but not deploying." + build_docs + exit $? fi # Adding GitHub pages branch. `git submodule add` checks it @@ -42,49 +46,51 @@ git submodule add -q -b gh-pages \ ${GH_PAGES_DIR} # Determine if we are building a new tag or are building docs -# for master. Then build new docset in docs/_build from master. -if [[ -z "${TRAVIS_TAG}" ]]; then - SPHINX_RELEASE=$(git log -1 --pretty=%h) tox -e docs -else +# for master. Then build new docs in docs/_build from master. +if [[ -n "${CIRCLE_TAG}" ]]; then # Sphinx will use the package version by default. - tox -e docs + build_docs +else + SPHINX_RELEASE=$(git log -1 --pretty=%h) build_docs fi # Get the current version. Assumes the PWD is the root of the git repo. -# We run this after `tox -e docs` to make sure the `docs` env is -# set up. -CURRENT_VERSION=$(.tox/docs/bin/python scripts/get_version.py) +# This is only likely to work from within nox, because the environment +# must be set up. +CURRENT_VERSION=$(python scripts/get_version.py) # Update gh-pages with the created docs. cd ${GH_PAGES_DIR} -if [[ -z "${TRAVIS_TAG}" ]]; then - git rm -fr latest/ - cp -R ../docs/_build/html/ latest/ -else +if [[ -n "${CIRCLE_TAG}" ]]; then if [[ -d ${CURRENT_VERSION} ]]; then echo "The directory ${CURRENT_VERSION} already exists." exit 1 fi git rm -fr stable/ + # Put the new release in stable and with the actual version. cp -R ../docs/_build/html/ stable/ cp -R ../docs/_build/html/ "${CURRENT_VERSION}/" +else + git rm -fr latest/ + cp -R ../docs/_build/html/ latest/ fi # Update the files push to gh-pages. git add . git status -# H/T: https://github.com/dhermes +# If there are no changes, just exit cleanly. if [[ -z "$(git status --porcelain)" ]]; then echo "Nothing to commit. Exiting without pushing changes." exit fi # Commit to gh-pages branch to apply changes. -git config --global user.email "travis@travis-ci.org" -git config --global user.name "travis-ci" +git config --global user.email "circle@circleci.com" +git config --global user.name "CircleCI" git commit -m "Update docs after merge to master." + # NOTE: This may fail if two docs updates (on merges to master) # happen in close proximity. git push -q \ diff --git a/test_utils/setup.py b/test_utils/setup.py new file mode 100644 index 000000000000..c293d1d5ac15 --- /dev/null +++ b/test_utils/setup.py @@ -0,0 +1,63 @@ +# Copyright 2017 Google Inc. +# +# Licensed 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. + +import os + +from setuptools import find_packages +from setuptools import setup + + +PACKAGE_ROOT = os.path.abspath(os.path.dirname(__file__)) + + +# NOTE: This is duplicated throughout and we should try to +# consolidate. +SETUP_BASE = { + 'author': 'Google Cloud Platform', + 'author_email': 'jjg+google-cloud-python@google.com', + 'scripts': [], + 'url': 'https://github.com/GoogleCloudPlatform/google-cloud-python', + 'license': 'Apache 2.0', + 'platforms': 'Posix; MacOS X; Windows', + 'include_package_data': True, + 'zip_safe': False, + 'classifiers': [ + 'Development Status :: 4 - Beta', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: Apache Software License', + 'Operating System :: OS Independent', + 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', + 'Topic :: Internet', + ], +} + + +REQUIREMENTS = [ + 'google-cloud-core >= 0.23.1, < 0.24dev', + 'google-auth >= 0.4.0', + 'six', +] + +setup( + name='google-cloud-testutils', + version='0.23.0', + description='System test utilities for google-cloud-python', + packages=find_packages(), + install_requires=REQUIREMENTS, + **SETUP_BASE +) diff --git a/test_utils/test_utils/__init__.py b/test_utils/test_utils/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/system_tests/retry.py b/test_utils/test_utils/retry.py similarity index 100% rename from system_tests/retry.py rename to test_utils/test_utils/retry.py diff --git a/system_tests/system_test_utils.py b/test_utils/test_utils/system.py similarity index 96% rename from system_tests/system_test_utils.py rename to test_utils/test_utils/system.py index 6ee041f666ec..480ded235faa 100644 --- a/system_tests/system_test_utils.py +++ b/test_utils/test_utils/system.py @@ -79,5 +79,4 @@ def unique_resource_id(delimiter='_'): if build_id == '': return '%s%d' % (delimiter, 1000 * time.time()) else: - return '%s%s%s%d' % (delimiter, build_id, - delimiter, time.time()) + return '%s%s%s%d' % (delimiter, build_id, delimiter, time.time()) diff --git a/tox.ini b/tox.ini deleted file mode 100644 index bf86d15715ac..000000000000 --- a/tox.ini +++ /dev/null @@ -1,319 +0,0 @@ -[tox] -envlist = - py27,py34,py35,cover,docs,lint - -[testing] -deps = - {toxinidir}/core - {toxinidir}/bigtable - {toxinidir}/storage - {toxinidir}/datastore - {toxinidir}/bigquery - {toxinidir}/pubsub - {toxinidir}/logging - {toxinidir}/dns - {toxinidir}/language - {toxinidir}/error_reporting - {toxinidir}/resource_manager - {toxinidir}/monitoring - {toxinidir}/vision - {toxinidir}/translate - {toxinidir}/speech - {toxinidir}/runtimeconfig - {toxinidir}/spanner - mock - pytest -passenv = - CI_* - CIRCLE* - GOOGLE_* - TRAVIS* -localdeps = - pip install --quiet --upgrade \ - {toxinidir}/core \ - {toxinidir}/bigtable \ - {toxinidir}/storage \ - {toxinidir}/datastore \ - {toxinidir}/bigquery \ - {toxinidir}/pubsub \ - {toxinidir}/logging \ - {toxinidir}/dns \ - {toxinidir}/language \ - {toxinidir}/error_reporting \ - {toxinidir}/resource_manager \ - {toxinidir}/monitoring \ - {toxinidir}/vision \ - {toxinidir}/translate \ - {toxinidir}/speech \ - {toxinidir}/runtimeconfig -covercmd = - py.test --quiet \ - --cov=google.cloud \ - --cov=unit_tests \ - --cov-config {toxinidir}/.coveragerc \ - core/unit_tests - py.test --quiet \ - --cov=google.cloud \ - --cov=unit_tests \ - --cov-append \ - --cov-config {toxinidir}/.coveragerc \ - bigtable/unit_tests - py.test --quiet \ - --cov=google.cloud \ - --cov=unit_tests \ - --cov-append \ - --cov-config {toxinidir}/.coveragerc \ - storage/unit_tests - py.test --quiet \ - --cov=google.cloud \ - --cov=unit_tests \ - --cov-append \ - --cov-config {toxinidir}/.coveragerc \ - datastore/unit_tests - py.test --quiet \ - --cov=google.cloud \ - --cov=unit_tests \ - --cov-append \ - --cov-config {toxinidir}/.coveragerc \ - bigquery/unit_tests - py.test --quiet \ - --cov=google.cloud \ - --cov=unit_tests \ - --cov-append \ - --cov-config {toxinidir}/.coveragerc \ - pubsub/unit_tests - py.test --quiet \ - --cov=google.cloud \ - --cov=unit_tests \ - --cov-append \ - --cov-config {toxinidir}/.coveragerc \ - logging/unit_tests - py.test --quiet \ - --cov=google.cloud \ - --cov=unit_tests \ - --cov-append \ - --cov-config {toxinidir}/.coveragerc \ - dns/unit_tests - py.test --quiet \ - --cov=google.cloud \ - --cov=unit_tests \ - --cov-append \ - --cov-config {toxinidir}/.coveragerc \ - language/unit_tests - py.test --quiet \ - --cov=google.cloud \ - --cov=unit_tests \ - --cov-append \ - --cov-config {toxinidir}/.coveragerc \ - error_reporting/unit_tests - py.test --quiet \ - --cov=google.cloud \ - --cov=unit_tests \ - --cov-append \ - --cov-config {toxinidir}/.coveragerc \ - resource_manager/unit_tests - py.test --quiet \ - --cov=google.cloud \ - --cov=unit_tests \ - --cov-append \ - --cov-config {toxinidir}/.coveragerc \ - monitoring/unit_tests - py.test --quiet \ - --cov=google.cloud \ - --cov=unit_tests \ - --cov-append \ - --cov-config {toxinidir}/.coveragerc \ - vision/unit_tests - py.test --quiet \ - --cov=google.cloud \ - --cov=unit_tests \ - --cov-append \ - --cov-config {toxinidir}/.coveragerc \ - translate/unit_tests - py.test --quiet \ - --cov=google.cloud \ - --cov=unit_tests \ - --cov-append \ - --cov-config {toxinidir}/.coveragerc \ - speech/unit_tests - py.test --quiet \ - --cov=google.cloud \ - --cov=unit_tests \ - --cov-append \ - --cov-config {toxinidir}/.coveragerc \ - runtimeconfig/unit_tests - py.test --quiet \ - --cov=google.cloud \ - --cov=unit_tests \ - --cov-append \ - --cov-config {toxinidir}/.coveragerc \ - spanner/unit_tests - coverage report --show-missing --fail-under=100 - -[testenv] -commands = - python {toxinidir}/scripts/run_unit_tests.py {posargs} -deps = -skip_install = - py27: True - py34: True - py35: True - cover: True - lint: True -passenv = - {[testing]passenv} - -[testenv:cover] -commands = - python {toxinidir}/scripts/run_unit_tests.py {posargs} --tox-env cover - -[testenv:umbrella-cover] -basepython = - python2.7 -commands = - {[testing]localdeps} - {[testing]covercmd} -deps = - {[testing]deps} - coverage - pytest-cov - -[testenv:coveralls] -basepython = {[testenv:umbrella-cover]basepython} -commands = - {[testing]localdeps} - {[testing]covercmd} - coveralls -ignore_errors = True -deps = - {[testenv:umbrella-cover]deps} - coveralls -passenv = - {[testing]passenv} - COVERALLS_REPO_TOKEN - -[testenv:json-docs] -basepython = - python2.7 -commands = - {[testing]localdeps} - python -c \ - "import shutil; shutil.rmtree('docs/_build/json', ignore_errors=True)" - {toxinidir}/scripts/update_json_docs.sh -deps = - parinx - pdoc - Sphinx -passenv = - TRAVIS_TAG - TRAVIS_BRANCH - TRAVIS_PULL_REQUEST - GH_OAUTH_TOKEN - -[testenv:docs] -basepython = - python2.7 -commands = - {[testing]localdeps} - python -c \ - "import shutil; shutil.rmtree('docs/_build', ignore_errors=True)" - sphinx-build -W -b html -d docs/_build/doctrees docs docs/_build/html - python {toxinidir}/scripts/verify_included_modules.py --build-root _build -deps = - {[testing]deps} - Sphinx - sphinx_rtd_theme -passenv = - {[testing]passenv} - SPHINX_RELEASE - READTHEDOCS - -[testenv:docs-doc2dash] -basepython = {[testenv:docs]basepython} -commands = - {[testing]localdeps} - sphinx-build -W -b html -d docs/_build_doc2dash/doctrees \ - docs docs/_build_doc2dash/html - doc2dash -vv --force -n google-cloud-python \ - -i docs/_static/images/gcp-logo-32x32.png -I index.html \ - -u https://google-cloud-python.readthedocs.io/en/latest/ \ - -d docs/_build_doc2dash/ docs/_build_doc2dash/html -deps = - {[testenv:docs]deps} - doc2dash -passenv = - {[testenv:docs]passenv} - -[pycodestyle] -exclude = - docs/conf.py, - bigtable/google/cloud/bigtable/_generated/*, -verbose = 1 - -[testenv:lint] -basepython = - python2.7 -commands = - python {toxinidir}/scripts/pycodestyle_on_repo.py - python {toxinidir}/scripts/run_pylint.py -deps = - {[testing]deps} - pycodestyle >= 2.1.0 - pylint >= 1.6.4 -passenv = - {[testing]passenv} - -[testenv:system-tests] -basepython = - python2.7 -commands = - {[testing]localdeps} - python {toxinidir}/system_tests/attempt_system_tests.py {posargs} -deps = - {[testing]deps} - Sphinx -passenv = - {[testing]passenv} - encrypted_* - -[testenv:system-tests3] -basepython = - python3.5 -commands = - {[testenv:system-tests]commands} -deps = - {[testenv:system-tests]deps} -passenv = - {[testenv:system-tests]passenv} - -[emulator] -deps = - {[testing]deps} - psutil -setenv = - GOOGLE_CLOUD_NO_PRINT=true -emulatorcmd = - python {toxinidir}/system_tests/run_emulator.py - -[testenv:datastore-emulator] -commands = - {[emulator]emulatorcmd} --package=datastore -setenv = {[emulator]setenv} -passenv = {[testing]passenv} -deps = - {[emulator]deps} - Sphinx - -[testenv:pubsub-emulator] -commands = - {[emulator]emulatorcmd} --package=pubsub -setenv = {[emulator]setenv} -passenv = {[testing]passenv} -deps = {[emulator]deps} - -[testenv:bigtable-emulator] -commands = - {[emulator]emulatorcmd} --package=bigtable -setenv = {[emulator]setenv} -passenv = {[testing]passenv} -deps = {[emulator]deps} diff --git a/translate/.flake8 b/translate/.flake8 new file mode 100644 index 000000000000..25168dc87605 --- /dev/null +++ b/translate/.flake8 @@ -0,0 +1,6 @@ +[flake8] +exclude = + __pycache__, + .git, + *.pyc, + conf.py diff --git a/translate/LICENSE b/translate/LICENSE new file mode 100644 index 000000000000..d64569567334 --- /dev/null +++ b/translate/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed 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. diff --git a/translate/MANIFEST.in b/translate/MANIFEST.in index cb3a2b9ef4fa..9f7100c9528a 100644 --- a/translate/MANIFEST.in +++ b/translate/MANIFEST.in @@ -1,4 +1,4 @@ -include README.rst -graft google -graft unit_tests -global-exclude *.pyc +include README.rst LICENSE +recursive-include google *.json *.proto +recursive-include unit_tests * +global-exclude *.pyc __pycache__ diff --git a/translate/google/cloud/translate/__init__.py b/translate/google/cloud/translate/__init__.py index c1cd5dd440f4..bf20faa86bdf 100644 --- a/translate/google/cloud/translate/__init__.py +++ b/translate/google/cloud/translate/__init__.py @@ -21,3 +21,6 @@ from google.cloud.translate.client import BASE from google.cloud.translate.client import Client from google.cloud.translate.client import NMT + + +__all__ = ['__version__', 'BASE', 'Client', 'NMT'] diff --git a/translate/nox.py b/translate/nox.py new file mode 100644 index 000000000000..65c195c9016f --- /dev/null +++ b/translate/nox.py @@ -0,0 +1,87 @@ +# Copyright 2016 Google Inc. +# +# Licensed 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. + +from __future__ import absolute_import + +import os + +import nox + + +@nox.session +@nox.parametrize('python_version', ['2.7', '3.4', '3.5', '3.6']) +def unit_tests(session, python_version): + """Run the unit test suite.""" + + # Run unit tests against all supported versions of Python. + session.interpreter = 'python%s' % python_version + + # Install all test dependencies, then install this package in-place. + session.install('mock', 'pytest', 'pytest-cov', '../core/') + session.install('-e', '.') + + # Run py.test against the unit tests. + session.run('py.test', '--quiet', + '--cov=google.cloud.translate', '--cov=tests.unit', '--cov-append', + '--cov-config=.coveragerc', '--cov-report=', '--cov-fail-under=97', + 'tests/unit', + ) + + +@nox.session +@nox.parametrize('python_version', ['2.7', '3.6']) +def system_tests(session, python_version): + """Run the system test suite.""" + + # Sanity check: Only run system tests if the environment variable is set. + if not os.environ.get('GOOGLE_APPLICATION_CREDENTIALS', ''): + return + + # Run the system tests against latest Python 2 and Python 3 only. + session.interpreter = 'python%s' % python_version + + # Install all test dependencies, then install this package into the + # virutalenv's dist-packages. + session.install('mock', 'pytest', + '../core/', '../test_utils/') + session.install('.') + + # Run py.test against the system tests. + session.run('py.test', '--quiet', 'tests/system.py') + + +@nox.session +def lint(session): + """Run flake8. + + Returns a failure if flake8 finds linting errors or sufficiently + serious code quality issues. + """ + session.interpreter = 'python3.6' + session.install('flake8') + session.install('.') + session.run('flake8', 'google/cloud/translate') + + +@nox.session +def cover(session): + """Run the final coverage report. + + This outputs the coverage report aggregating coverage from the unit + test runs (not system test runs), and then erases coverage data. + """ + session.interpreter = 'python3.6' + session.install('coverage', 'pytest-cov') + session.run('coverage', 'report', '--show-missing', '--fail-under=100') + session.run('coverage', 'erase') diff --git a/translate/setup.py b/translate/setup.py index 923a2132d20f..9fa55a825f7f 100644 --- a/translate/setup.py +++ b/translate/setup.py @@ -62,7 +62,7 @@ 'google', 'google.cloud', ], - packages=find_packages(), + packages=find_packages(exclude=('unit_tests*',)), install_requires=REQUIREMENTS, **SETUP_BASE ) diff --git a/translate/tests/__init__.py b/translate/tests/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/system_tests/translate.py b/translate/tests/system.py similarity index 100% rename from system_tests/translate.py rename to translate/tests/system.py diff --git a/translate/unit_tests/__init__.py b/translate/tests/unit/__init__.py similarity index 100% rename from translate/unit_tests/__init__.py rename to translate/tests/unit/__init__.py diff --git a/translate/unit_tests/test__http.py b/translate/tests/unit/test__http.py similarity index 100% rename from translate/unit_tests/test__http.py rename to translate/tests/unit/test__http.py diff --git a/translate/unit_tests/test_client.py b/translate/tests/unit/test_client.py similarity index 100% rename from translate/unit_tests/test_client.py rename to translate/tests/unit/test_client.py diff --git a/translate/tox.ini b/translate/tox.ini deleted file mode 100644 index c51673432353..000000000000 --- a/translate/tox.ini +++ /dev/null @@ -1,35 +0,0 @@ -[tox] -envlist = - py27,py34,py35,cover - -[testing] -localdeps = - pip install --quiet --upgrade {toxinidir}/../core -deps = - {toxinidir}/../core - mock - pytest -covercmd = - py.test --quiet \ - --cov=google.cloud.translate \ - --cov=unit_tests \ - --cov-config {toxinidir}/.coveragerc \ - unit_tests - -[testenv] -commands = - {[testing]localdeps} - py.test --quiet {posargs} unit_tests -deps = - {[testing]deps} - -[testenv:cover] -basepython = - python2.7 -commands = - {[testing]localdeps} - {[testing]covercmd} -deps = - {[testenv]deps} - coverage - pytest-cov diff --git a/vision/.flake8 b/vision/.flake8 new file mode 100644 index 000000000000..25168dc87605 --- /dev/null +++ b/vision/.flake8 @@ -0,0 +1,6 @@ +[flake8] +exclude = + __pycache__, + .git, + *.pyc, + conf.py diff --git a/vision/LICENSE b/vision/LICENSE new file mode 100644 index 000000000000..d64569567334 --- /dev/null +++ b/vision/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed 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. diff --git a/vision/MANIFEST.in b/vision/MANIFEST.in index cb3a2b9ef4fa..9f7100c9528a 100644 --- a/vision/MANIFEST.in +++ b/vision/MANIFEST.in @@ -1,4 +1,4 @@ -include README.rst -graft google -graft unit_tests -global-exclude *.pyc +include README.rst LICENSE +recursive-include google *.json *.proto +recursive-include unit_tests * +global-exclude *.pyc __pycache__ diff --git a/vision/google/cloud/vision/__init__.py b/vision/google/cloud/vision/__init__.py index 036cd9c08e53..c4354c17b571 100644 --- a/vision/google/cloud/vision/__init__.py +++ b/vision/google/cloud/vision/__init__.py @@ -19,3 +19,6 @@ __version__ = get_distribution('google-cloud-vision').version from google.cloud.vision.client import Client + + +__all__ = ['__version__', 'Client'] diff --git a/vision/nox.py b/vision/nox.py new file mode 100644 index 000000000000..a8b8d9314563 --- /dev/null +++ b/vision/nox.py @@ -0,0 +1,88 @@ +# Copyright 2016 Google Inc. +# +# Licensed 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. + +from __future__ import absolute_import + +import os + +import nox + + +@nox.session +@nox.parametrize('python_version', ['2.7', '3.4', '3.5', '3.6']) +def unit_tests(session, python_version): + """Run the unit test suite.""" + + # Run unit tests against all supported versions of Python. + session.interpreter = 'python%s' % python_version + + # Install all test dependencies, then install this package in-place. + session.install('mock', 'pytest', 'pytest-cov', '../core/') + session.install('-e', '.') + + # Run py.test against the unit tests. + session.run('py.test', '--quiet', + '--cov=google.cloud.vision', '--cov=tests.unit', '--cov-append', + '--cov-config=.coveragerc', '--cov-report=', '--cov-fail-under=97', + 'tests/unit', + ) + + +@nox.session +@nox.parametrize('python_version', ['2.7', '3.6']) +def system_tests(session, python_version): + """Run the system test suite.""" + + # Sanity check: Only run system tests if the environment variable is set. + if not os.environ.get('GOOGLE_APPLICATION_CREDENTIALS', ''): + return + + # Run the system tests against latest Python 2 and Python 3 only. + session.interpreter = 'python%s' % python_version + + # Install all test dependencies, then install this package into the + # virutalenv's dist-packages. + session.install('mock', 'pytest', + '../core/', '../test_utils/', '../storage/') + session.install('.') + + # Run py.test against the system tests. + session.run('py.test', '--quiet', 'tests/system.py') + + +@nox.session +def lint(session): + """Run flake8. + + Returns a failure if flake8 finds linting errors or sufficiently + serious code quality issues. + """ + session.interpreter = 'python3.6' + session.install('flake8') + session.install('.') + session.run('flake8', 'google/cloud/vision') + + +@nox.session +def cover(session): + """Run the final coverage report. + + This outputs the coverage report aggregating coverage from the unit + test runs (not system test runs), and then erases coverage data. + """ + session.interpreter = 'python3.6' + session.chdir(os.path.dirname(__file__)) + session.install('coverage', 'pytest-cov') + session.run('coverage', 'report', '--show-missing', '--fail-under=100') + session.run('coverage', 'erase') diff --git a/vision/setup.py b/vision/setup.py index a88924456342..56dc50646c68 100644 --- a/vision/setup.py +++ b/vision/setup.py @@ -64,7 +64,7 @@ 'google', 'google.cloud', ], - packages=find_packages(), + packages=find_packages(exclude=('unit_tests*',)), install_requires=REQUIREMENTS, **SETUP_BASE ) diff --git a/vision/tests/__init__.py b/vision/tests/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/system_tests/data/car.jpg b/vision/tests/data/car.jpg similarity index 100% rename from system_tests/data/car.jpg rename to vision/tests/data/car.jpg diff --git a/system_tests/data/faces.jpg b/vision/tests/data/faces.jpg similarity index 100% rename from system_tests/data/faces.jpg rename to vision/tests/data/faces.jpg diff --git a/system_tests/data/full-text.jpg b/vision/tests/data/full-text.jpg similarity index 100% rename from system_tests/data/full-text.jpg rename to vision/tests/data/full-text.jpg diff --git a/system_tests/data/landmark.jpg b/vision/tests/data/landmark.jpg similarity index 100% rename from system_tests/data/landmark.jpg rename to vision/tests/data/landmark.jpg diff --git a/system_tests/data/logo.png b/vision/tests/data/logo.png similarity index 100% rename from system_tests/data/logo.png rename to vision/tests/data/logo.png diff --git a/system_tests/data/text.jpg b/vision/tests/data/text.jpg similarity index 100% rename from system_tests/data/text.jpg rename to vision/tests/data/text.jpg diff --git a/system_tests/vision.py b/vision/tests/system.py similarity index 99% rename from system_tests/vision.py rename to vision/tests/system.py index b0b93591d611..1c9770aa57b9 100644 --- a/system_tests/vision.py +++ b/vision/tests/system.py @@ -17,6 +17,8 @@ import os import unittest +import six + from google.cloud import exceptions from google.cloud import storage from google.cloud import vision @@ -24,12 +26,11 @@ from google.cloud.vision.feature import Feature from google.cloud.vision.feature import FeatureTypes -from system_test_utils import unique_resource_id -from retry import RetryErrors -import six +from test_utils.retry import RetryErrors +from test_utils.system import unique_resource_id -_SYS_TESTS_DIR = os.path.abspath(os.path.dirname(__file__)) +_SYS_TESTS_DIR = os.path.realpath(os.path.dirname(__file__)) LOGO_FILE = os.path.join(_SYS_TESTS_DIR, 'data', 'logo.png') FACE_FILE = os.path.join(_SYS_TESTS_DIR, 'data', 'faces.jpg') LABEL_FILE = os.path.join(_SYS_TESTS_DIR, 'data', 'car.jpg') @@ -141,7 +142,7 @@ def _assert_crop_hint(self, hint): self.assertIsInstance(hint, CropHint) self.assertIsInstance(hint.bounds, Bounds) - self.assertGreater(hint.bounds.vertices, 1) + self.assertGreater(len(hint.bounds.vertices), 1) self.assertIsInstance(hint.confidence, (int, float)) self.assertIsInstance(hint.importance_fraction, float) diff --git a/vision/unit_tests/__init__.py b/vision/tests/unit/__init__.py similarity index 100% rename from vision/unit_tests/__init__.py rename to vision/tests/unit/__init__.py diff --git a/vision/unit_tests/_fixtures.py b/vision/tests/unit/_fixtures.py similarity index 100% rename from vision/unit_tests/_fixtures.py rename to vision/tests/unit/_fixtures.py diff --git a/vision/unit_tests/test__gax.py b/vision/tests/unit/test__gax.py similarity index 100% rename from vision/unit_tests/test__gax.py rename to vision/tests/unit/test__gax.py diff --git a/vision/unit_tests/test__http.py b/vision/tests/unit/test__http.py similarity index 99% rename from vision/unit_tests/test__http.py rename to vision/tests/unit/test__http.py index cfecc6d98b29..ee486e409b8a 100644 --- a/vision/unit_tests/test__http.py +++ b/vision/tests/unit/test__http.py @@ -147,7 +147,7 @@ def test_call_annotate_with_more_than_one_result(self): from google.cloud.vision.feature import FeatureTypes from google.cloud.vision.image import Image from google.cloud.vision.likelihood import Likelihood - from unit_tests._fixtures import MULTIPLE_RESPONSE + from tests.unit._fixtures import MULTIPLE_RESPONSE client = mock.Mock(spec_set=['_connection']) feature = Feature(FeatureTypes.LABEL_DETECTION, 5) diff --git a/vision/unit_tests/test_annotations.py b/vision/tests/unit/test_annotations.py similarity index 100% rename from vision/unit_tests/test_annotations.py rename to vision/tests/unit/test_annotations.py diff --git a/vision/unit_tests/test_batch.py b/vision/tests/unit/test_batch.py similarity index 100% rename from vision/unit_tests/test_batch.py rename to vision/tests/unit/test_batch.py diff --git a/vision/unit_tests/test_client.py b/vision/tests/unit/test_client.py similarity index 96% rename from vision/unit_tests/test_client.py rename to vision/tests/unit/test_client.py index b5c595d2443e..9df21c6420e7 100644 --- a/vision/unit_tests/test_client.py +++ b/vision/tests/unit/test_client.py @@ -81,7 +81,7 @@ def test_make_http_client(self): def test_face_annotation(self): from google.cloud.vision.annotations import Annotations from google.cloud.vision.feature import Feature, FeatureTypes - from unit_tests._fixtures import FACE_DETECTION_RESPONSE + from tests.unit._fixtures import FACE_DETECTION_RESPONSE returned = FACE_DETECTION_RESPONSE request = { @@ -157,8 +157,8 @@ def test_multiple_detection_from_content(self): import copy from google.cloud.vision.feature import Feature from google.cloud.vision.feature import FeatureTypes - from unit_tests._fixtures import LABEL_DETECTION_RESPONSE - from unit_tests._fixtures import LOGO_DETECTION_RESPONSE + from tests.unit._fixtures import LABEL_DETECTION_RESPONSE + from tests.unit._fixtures import LOGO_DETECTION_RESPONSE returned = copy.deepcopy(LABEL_DETECTION_RESPONSE) logos = copy.deepcopy(LOGO_DETECTION_RESPONSE['responses'][0]) @@ -214,7 +214,7 @@ def test_multiple_detection_from_content(self): def test_detect_crop_hints_from_source(self): from google.cloud.vision.crop_hint import CropHint - from unit_tests._fixtures import CROP_HINTS_RESPONSE + from tests.unit._fixtures import CROP_HINTS_RESPONSE credentials = _make_credentials() client = self._make_one( @@ -237,7 +237,7 @@ def test_detect_crop_hints_from_source(self): def test_face_detection_from_source(self): from google.cloud.vision.face import Face - from unit_tests._fixtures import FACE_DETECTION_RESPONSE + from tests.unit._fixtures import FACE_DETECTION_RESPONSE credentials = _make_credentials() client = self._make_one( @@ -259,7 +259,7 @@ def test_face_detection_from_source(self): def test_face_detection_from_content(self): from google.cloud.vision.face import Face - from unit_tests._fixtures import FACE_DETECTION_RESPONSE + from tests.unit._fixtures import FACE_DETECTION_RESPONSE credentials = _make_credentials() client = self._make_one( @@ -300,7 +300,7 @@ def test_face_detection_from_content_no_results(self): def test_detect_full_text_annotation(self): from google.cloud.vision.text import TextAnnotation - from unit_tests._fixtures import FULL_TEXT_RESPONSE + from tests.unit._fixtures import FULL_TEXT_RESPONSE returned = FULL_TEXT_RESPONSE credentials = _make_credentials() @@ -335,7 +335,7 @@ def test_detect_full_text_annotation(self): def test_label_detection_from_source(self): from google.cloud.vision.entity import EntityAnnotation - from unit_tests._fixtures import LABEL_DETECTION_RESPONSE + from tests.unit._fixtures import LABEL_DETECTION_RESPONSE credentials = _make_credentials() client = self._make_one( @@ -375,7 +375,7 @@ def test_label_detection_no_results(self): def test_landmark_detection_from_source(self): from google.cloud.vision.entity import EntityAnnotation - from unit_tests._fixtures import LANDMARK_DETECTION_RESPONSE + from tests.unit._fixtures import LANDMARK_DETECTION_RESPONSE credentials = _make_credentials() client = self._make_one(project=PROJECT, credentials=credentials, @@ -401,7 +401,7 @@ def test_landmark_detection_from_source(self): def test_landmark_detection_from_content(self): from google.cloud.vision.entity import EntityAnnotation - from unit_tests._fixtures import LANDMARK_DETECTION_RESPONSE + from tests.unit._fixtures import LANDMARK_DETECTION_RESPONSE credentials = _make_credentials() client = self._make_one( @@ -436,7 +436,7 @@ def test_landmark_detection_no_results(self): def test_logo_detection_from_source(self): from google.cloud.vision.entity import EntityAnnotation - from unit_tests._fixtures import LOGO_DETECTION_RESPONSE + from tests.unit._fixtures import LOGO_DETECTION_RESPONSE credentials = _make_credentials() client = self._make_one( @@ -457,7 +457,7 @@ def test_logo_detection_from_source(self): def test_logo_detection_from_content(self): from google.cloud.vision.entity import EntityAnnotation - from unit_tests._fixtures import LOGO_DETECTION_RESPONSE + from tests.unit._fixtures import LOGO_DETECTION_RESPONSE credentials = _make_credentials() client = self._make_one( @@ -477,7 +477,7 @@ def test_logo_detection_from_content(self): def test_text_detection_from_source(self): from google.cloud.vision.entity import EntityAnnotation - from unit_tests._fixtures import TEXT_DETECTION_RESPONSE + from tests.unit._fixtures import TEXT_DETECTION_RESPONSE credentials = _make_credentials() client = self._make_one( @@ -503,7 +503,7 @@ def test_text_detection_from_source(self): def test_safe_search_detection_from_source(self): from google.cloud.vision.likelihood import Likelihood from google.cloud.vision.safe_search import SafeSearchAnnotation - from unit_tests._fixtures import SAFE_SEARCH_DETECTION_RESPONSE + from tests.unit._fixtures import SAFE_SEARCH_DETECTION_RESPONSE credentials = _make_credentials() client = self._make_one( @@ -541,7 +541,7 @@ def test_safe_search_no_results(self): def test_image_properties_detection_from_source(self): from google.cloud.vision.color import ImagePropertiesAnnotation - from unit_tests._fixtures import IMAGE_PROPERTIES_RESPONSE + from tests.unit._fixtures import IMAGE_PROPERTIES_RESPONSE credentials = _make_credentials() client = self._make_one( @@ -583,7 +583,7 @@ def test_detect_web_detection(self): from google.cloud.vision.web import WebEntity from google.cloud.vision.web import WebImage from google.cloud.vision.web import WebPage - from unit_tests._fixtures import WEB_DETECTION_RESPONSE + from tests.unit._fixtures import WEB_DETECTION_RESPONSE credentials = _make_credentials() client = self._make_one( diff --git a/vision/unit_tests/test_color.py b/vision/tests/unit/test_color.py similarity index 100% rename from vision/unit_tests/test_color.py rename to vision/tests/unit/test_color.py diff --git a/vision/unit_tests/test_crop_hint.py b/vision/tests/unit/test_crop_hint.py similarity index 97% rename from vision/unit_tests/test_crop_hint.py rename to vision/tests/unit/test_crop_hint.py index c9449617b012..15f10ce1eec2 100644 --- a/vision/unit_tests/test_crop_hint.py +++ b/vision/tests/unit/test_crop_hint.py @@ -23,7 +23,7 @@ def _get_target_class(): return CropHint def test_crop_hint_annotation(self): - from unit_tests._fixtures import CROP_HINTS_RESPONSE + from tests.unit._fixtures import CROP_HINTS_RESPONSE from google.cloud.vision.geometry import Bounds response = CROP_HINTS_RESPONSE['responses'][0]['cropHintsAnnotation'] diff --git a/vision/unit_tests/test_entity.py b/vision/tests/unit/test_entity.py similarity index 97% rename from vision/unit_tests/test_entity.py rename to vision/tests/unit/test_entity.py index bca67270226b..d5b0465d31c9 100644 --- a/vision/unit_tests/test_entity.py +++ b/vision/tests/unit/test_entity.py @@ -22,7 +22,7 @@ def _get_target_class(): return EntityAnnotation def test_logo_annotation(self): - from unit_tests._fixtures import LOGO_DETECTION_RESPONSE + from tests.unit._fixtures import LOGO_DETECTION_RESPONSE entity_class = self._get_target_class() logo = entity_class.from_api_repr( diff --git a/vision/unit_tests/test_face.py b/vision/tests/unit/test_face.py similarity index 98% rename from vision/unit_tests/test_face.py rename to vision/tests/unit/test_face.py index 6f7864e5f750..8773a00764de 100644 --- a/vision/unit_tests/test_face.py +++ b/vision/tests/unit/test_face.py @@ -28,7 +28,7 @@ def _make_face_pb(self, *args, **kwargs): return image_annotator_pb2.FaceAnnotation(*args, **kwargs) def setUp(self): - from unit_tests._fixtures import FACE_DETECTION_RESPONSE + from tests.unit._fixtures import FACE_DETECTION_RESPONSE self.face_annotations = FACE_DETECTION_RESPONSE['responses'][0] self.face_class = self._get_target_class() diff --git a/vision/unit_tests/test_feature.py b/vision/tests/unit/test_feature.py similarity index 100% rename from vision/unit_tests/test_feature.py rename to vision/tests/unit/test_feature.py diff --git a/vision/unit_tests/test_geometry.py b/vision/tests/unit/test_geometry.py similarity index 100% rename from vision/unit_tests/test_geometry.py rename to vision/tests/unit/test_geometry.py diff --git a/vision/unit_tests/test_image.py b/vision/tests/unit/test_image.py similarity index 100% rename from vision/unit_tests/test_image.py rename to vision/tests/unit/test_image.py diff --git a/vision/unit_tests/test_safe_search.py b/vision/tests/unit/test_safe_search.py similarity index 97% rename from vision/unit_tests/test_safe_search.py rename to vision/tests/unit/test_safe_search.py index a1d4e49ce062..4d6d2882cb98 100644 --- a/vision/unit_tests/test_safe_search.py +++ b/vision/tests/unit/test_safe_search.py @@ -24,7 +24,7 @@ def _get_target_class(): def test_safe_search_annotation(self): from google.cloud.vision.likelihood import Likelihood - from unit_tests._fixtures import SAFE_SEARCH_DETECTION_RESPONSE + from tests.unit._fixtures import SAFE_SEARCH_DETECTION_RESPONSE response = SAFE_SEARCH_DETECTION_RESPONSE['responses'][0] safe_search_response = response['safeSearchAnnotation'] diff --git a/vision/unit_tests/test_text.py b/vision/tests/unit/test_text.py similarity index 100% rename from vision/unit_tests/test_text.py rename to vision/tests/unit/test_text.py diff --git a/vision/unit_tests/test_web.py b/vision/tests/unit/test_web.py similarity index 100% rename from vision/unit_tests/test_web.py rename to vision/tests/unit/test_web.py diff --git a/vision/tox.ini b/vision/tox.ini deleted file mode 100644 index e0d8c2b172de..000000000000 --- a/vision/tox.ini +++ /dev/null @@ -1,35 +0,0 @@ -[tox] -envlist = - py27,py34,py35,cover - -[testing] -localdeps = - pip install --quiet --upgrade {toxinidir}/../core -deps = - {toxinidir}/../core - mock - pytest -covercmd = - py.test --quiet \ - --cov=google.cloud.vision \ - --cov=unit_tests \ - --cov-config {toxinidir}/.coveragerc \ - unit_tests - -[testenv] -commands = - {[testing]localdeps} - py.test --quiet {posargs} unit_tests -deps = - {[testing]deps} - -[testenv:cover] -basepython = - python2.7 -commands = - {[testing]localdeps} - {[testing]covercmd} -deps = - {[testenv]deps} - coverage - pytest-cov