From 174ed5f49655360add738184a2b86398cdaeb660 Mon Sep 17 00:00:00 2001 From: Julien Nakache Date: Wed, 27 Mar 2019 17:25:33 -0400 Subject: [PATCH 1/2] fix ci, set up tox, set up pre-commit, various cleanups --- .pre-commit-config.yaml | 18 +++++ .travis.yml | 77 +++++++++++---------- README.md | 18 +++-- examples/nameko_sqlalchemy/README.md | 2 +- examples/nameko_sqlalchemy/app.py | 2 +- examples/nameko_sqlalchemy/config.yml | 2 +- examples/nameko_sqlalchemy/requirements.txt | 2 +- examples/nameko_sqlalchemy/run.sh | 2 +- examples/nameko_sqlalchemy/service.py | 2 +- graphene_sqlalchemy/tests/test_converter.py | 3 +- graphene_sqlalchemy/tests/test_query.py | 1 - graphene_sqlalchemy/tests/test_reflected.py | 1 - graphene_sqlalchemy/tests/test_schema.py | 1 - graphene_sqlalchemy/tests/test_types.py | 6 +- graphene_sqlalchemy/types.py | 8 ++- setup.cfg | 21 +----- setup.py | 26 +++++-- tox.ini | 20 ++++++ 18 files changed, 131 insertions(+), 81 deletions(-) create mode 100644 .pre-commit-config.yaml create mode 100644 tox.ini diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..9084d9c6 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,18 @@ +default_language_version: + python: python3.7 +repos: +- repo: git://github.com/pre-commit/pre-commit-hooks + rev: c8bad492e1b1d65d9126dba3fe3bd49a5a52b9d6 # v2.1.0 + hooks: + - id: check-merge-conflict + - id: check-yaml + - id: debug-statements + - id: end-of-file-fixer + exclude: ^docs/.*$ + # TODO Enable in its own PR + # - id: trailing-whitespace + # exclude: README.md +- repo: git://github.com/PyCQA/flake8 + rev: 88caf5ac484f5c09aedc02167c59c66ff0af0068 # 3.7.7 + hooks: + - id: flake8 diff --git a/.travis.yml b/.travis.yml index dd80108a..33704975 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,42 +1,47 @@ language: python -sudo: false -python: -- 2.7 -- 3.4 -- 3.5 -- 3.6 -before_install: -install: -- | - if [ "$TEST_TYPE" = build ]; then - pip install pytest==3.0.2 pytest-cov pytest-benchmark coveralls six mock sqlalchemy_utils - pip install -e . - python setup.py develop - elif [ "$TEST_TYPE" = lint ]; then - pip install flake8 - fi -script: -- | - if [ "$TEST_TYPE" = lint ]; then - echo "Checking Python code lint." - flake8 graphene_sqlalchemy - exit - elif [ "$TEST_TYPE" = build ]; then - py.test --cov=graphene_sqlalchemy graphene_sqlalchemy examples - fi -after_success: -- | - if [ "$TEST_TYPE" = build ]; then - coveralls - fi -env: - matrix: - - TEST_TYPE=build matrix: - fast_finish: true include: - - python: '2.7' - env: TEST_TYPE=lint + # Python 2.7 + # TODO Fix enum and add back tests for py27 + # See https://github.com/graphql-python/graphene-sqlalchemy/pull/177 + # - env: TOXENV=py27 + # python: 2.7 + # Python 3.5 + - env: TOXENV=py34 + python: 3.4 + # Python 3.5 + - env: TOXENV=py35 + python: 3.5 + # Python 3.6 + - env: TOXENV=py36 + python: 3.6 + # Python 3.7 + - env: TOXENV=py37 + python: 3.7 + dist: xenial + # SQLAlchemy 1.1 + - env: TOXENV=py37-sql11 + python: 3.7 + dist: xenial + # SQLAlchemy 1.2 + - env: TOXENV=py37-sql12 + python: 3.7 + dist: xenial + # SQLAlchemy 1.3 + - env: TOXENV=py37-sql13 + python: 3.7 + dist: xenial + # Pre-commit + - env: TOXENV=pre-commit + python: 3.7 + dist: xenial +install: pip install .[dev] +script: tox +after_success: coveralls +cache: + directories: + - $HOME/.cache/pip + - $HOME/.cache/pre-commit deploy: provider: pypi user: syrusakbary diff --git a/README.md b/README.md index 91a5349f..2effbe3b 100644 --- a/README.md +++ b/README.md @@ -112,14 +112,24 @@ To learn more check out the following [examples](examples/): ## Contributing -After cloning this repo, ensure dependencies are installed by running: +Set up our development dependencies: ```sh -python setup.py install +pip install -e ".[dev]" +pre-commit install ``` -After developing, the full test suite can be evaluated by running: +We use `tox` to test this library against different versions of `python` and `SQLAlchemy`. +While developping locally, it is usually fine to run the tests against the most recent versions: ```sh -python setup.py test # Use --pytest-args="-v -s" for verbose mode +tox -e py37 # Python 3.7, SQLAlchemy < 2.0 +tox -e py37 -- -v -s # Verbose output +tox -e py37 -- -k test_query # Only test_query.py +``` + +Our linters will run automatically when committing via git hooks but you can also run them manually: + +```sh +tox -e pre-commit ``` diff --git a/examples/nameko_sqlalchemy/README.md b/examples/nameko_sqlalchemy/README.md index f60ccb00..39cfe925 100644 --- a/examples/nameko_sqlalchemy/README.md +++ b/examples/nameko_sqlalchemy/README.md @@ -51,4 +51,4 @@ Now the following command will setup the database, and start the server: Now head on over to postman and send POST request to: [http://127.0.0.1:5000/graphql](http://127.0.0.1:5000/graphql) -and run some queries! \ No newline at end of file +and run some queries! diff --git a/examples/nameko_sqlalchemy/app.py b/examples/nameko_sqlalchemy/app.py index f7fe150a..3080d56c 100755 --- a/examples/nameko_sqlalchemy/app.py +++ b/examples/nameko_sqlalchemy/app.py @@ -33,4 +33,4 @@ def parse_body(self,request): elif content_type in ('application/x-www-form-urlencoded', 'multipart/form-data'): return request.form - return {} \ No newline at end of file + return {} diff --git a/examples/nameko_sqlalchemy/config.yml b/examples/nameko_sqlalchemy/config.yml index cdea553c..8ca6a45c 100644 --- a/examples/nameko_sqlalchemy/config.yml +++ b/examples/nameko_sqlalchemy/config.yml @@ -1 +1 @@ -WEB_SERVER_ADDRESS: '0.0.0.0:5000' \ No newline at end of file +WEB_SERVER_ADDRESS: '0.0.0.0:5000' diff --git a/examples/nameko_sqlalchemy/requirements.txt b/examples/nameko_sqlalchemy/requirements.txt index 1f7be7db..be037f73 100644 --- a/examples/nameko_sqlalchemy/requirements.txt +++ b/examples/nameko_sqlalchemy/requirements.txt @@ -1,4 +1,4 @@ graphene[sqlalchemy] SQLAlchemy==1.0.11 nameko -graphql-server-core \ No newline at end of file +graphql-server-core diff --git a/examples/nameko_sqlalchemy/run.sh b/examples/nameko_sqlalchemy/run.sh index d3953677..bfe17d9a 100755 --- a/examples/nameko_sqlalchemy/run.sh +++ b/examples/nameko_sqlalchemy/run.sh @@ -1,4 +1,4 @@ #!/bin/sh echo "Starting application service server" # Run Service -nameko run --config config.yml service \ No newline at end of file +nameko run --config config.yml service diff --git a/examples/nameko_sqlalchemy/service.py b/examples/nameko_sqlalchemy/service.py index 394764e4..2188516f 100644 --- a/examples/nameko_sqlalchemy/service.py +++ b/examples/nameko_sqlalchemy/service.py @@ -8,4 +8,4 @@ class DepartmentService: @http('POST', '/graphql') def query(self, request): - return App().query(request) \ No newline at end of file + return App().query(request) diff --git a/graphene_sqlalchemy/tests/test_converter.py b/graphene_sqlalchemy/tests/test_converter.py index c2ec3e49..c6c49bba 100644 --- a/graphene_sqlalchemy/tests/test_converter.py +++ b/graphene_sqlalchemy/tests/test_converter.py @@ -168,7 +168,7 @@ class Test(Base): ) graphene_type = convert_sqlalchemy_column(Test.column) - assert graphene_type.kwargs["required"] == False + assert not graphene_type.kwargs["required"] def test_should_scalar_list_convert_list(): @@ -343,4 +343,3 @@ def __init__(self, col1, col2): ) assert "Don't know how to convert the composite field" in str(excinfo.value) - diff --git a/graphene_sqlalchemy/tests/test_query.py b/graphene_sqlalchemy/tests/test_query.py index f1116d9d..8bb4891c 100644 --- a/graphene_sqlalchemy/tests/test_query.py +++ b/graphene_sqlalchemy/tests/test_query.py @@ -555,4 +555,3 @@ def makeNodes(nodeList): assert set(node["node"]["name"] for node in value["edges"]) == set( node["node"]["name"] for node in expectedNoSort[key]["edges"] ) - diff --git a/graphene_sqlalchemy/tests/test_reflected.py b/graphene_sqlalchemy/tests/test_reflected.py index c8a1a70f..46e10de9 100644 --- a/graphene_sqlalchemy/tests/test_reflected.py +++ b/graphene_sqlalchemy/tests/test_reflected.py @@ -18,4 +18,3 @@ def test_objecttype_registered(): assert issubclass(Reflected, ObjectType) assert Reflected._meta.model == ReflectedEditor assert list(Reflected._meta.fields.keys()) == ["editor_id", "name"] - diff --git a/graphene_sqlalchemy/tests/test_schema.py b/graphene_sqlalchemy/tests/test_schema.py index 909570a6..628da185 100644 --- a/graphene_sqlalchemy/tests/test_schema.py +++ b/graphene_sqlalchemy/tests/test_schema.py @@ -47,4 +47,3 @@ class Meta: model = Reporter only_fields = ("id", "email") assert list(Reporter2._meta.fields.keys()) == ["id", "email"] - diff --git a/graphene_sqlalchemy/tests/test_types.py b/graphene_sqlalchemy/tests/test_types.py index c11ec351..183234e9 100644 --- a/graphene_sqlalchemy/tests/test_types.py +++ b/graphene_sqlalchemy/tests/test_types.py @@ -1,7 +1,7 @@ from collections import OrderedDict from graphene import Field, Int, Interface, ObjectType from graphene.relay import Node, is_node, Connection -import six +import six # noqa: F401 from promise import Promise from ..registry import Registry @@ -176,7 +176,9 @@ class TestConnection(Connection): class Meta: node = ReporterWithCustomOptions - resolver = lambda *args, **kwargs: Promise.resolve([]) + def resolver(*args, **kwargs): + return Promise.resolve([]) + result = SQLAlchemyConnectionField.connection_resolver( resolver, TestConnection, ReporterWithCustomOptions, None, None ) diff --git a/graphene_sqlalchemy/types.py b/graphene_sqlalchemy/types.py index e8a05c8f..9c093e59 100644 --- a/graphene_sqlalchemy/types.py +++ b/graphene_sqlalchemy/types.py @@ -1,9 +1,11 @@ from collections import OrderedDict +import sqlalchemy from sqlalchemy.inspection import inspect as sqlalchemyinspect from sqlalchemy.ext.hybrid import hybrid_property from sqlalchemy.orm.exc import NoResultFound +import graphene from graphene import Field # , annotate, ResolveInfo from graphene.relay import Connection, Node from graphene.types.objecttype import ObjectType, ObjectTypeOptions @@ -80,9 +82,9 @@ def construct_fields(model, registry, only_fields, exclude_fields): class SQLAlchemyObjectTypeOptions(ObjectTypeOptions): - model = None # type: Model - registry = None # type: Registry - connection = None # type: Type[Connection] + model = None # type: sqlalchemy.Model + registry = None # type: sqlalchemy.Registry + connection = None # type: graphene.Type[Connection] id = None # type: str diff --git a/setup.cfg b/setup.cfg index d8d54e3f..4e863d7a 100644 --- a/setup.cfg +++ b/setup.cfg @@ -2,28 +2,9 @@ exclude = setup.py,docs/*,examples/*,tests max-line-length = 120 -[coverage:run] -omit = */tests/* - +# TODO Add isort as a pre-commit hook [isort] known_first_party=graphene,graphene_sqlalchemy -[tool:pytest] -testpaths = graphene_sqlalchemy/ -addopts = - -s - ; --cov graphene-sqlalchemy -norecursedirs = - __pycache__ - *.egg-info - .cache - .git - .tox - appdir - docs -filterwarnings = - error - ignore::DeprecationWarning - [bdist_wheel] universal=1 diff --git a/setup.py b/setup.py index 7a6941b8..cfbae111 100644 --- a/setup.py +++ b/setup.py @@ -10,6 +10,12 @@ ast.literal_eval(_version_re.search(f.read().decode("utf-8")).group(1)) ) +tests_require = [ + "pytest==4.3.1", + "mock==2.0.0", + "pytest-cov==2.6.1", + "sqlalchemy_utils==0.33.9", +] setup( name="graphene-sqlalchemy", @@ -37,10 +43,20 @@ keywords="api graphql protocol rest relay graphene", packages=find_packages(exclude=["tests"]), install_requires=[ - "six>=1.10.0", - "graphene>=2.1.3", - "SQLAlchemy", - "singledispatch>=3.4.0.3", + # To keep things simple, we only support newer versions of Graphene + "graphene>=2.1.3,<3", + # Tests fail with 1.0.19 + "SQLAlchemy>=1.1,<2", + "six>=1.10.0,<2", + "singledispatch>=3.4.0.3,<4", ], - tests_require=["pytest>=2.7.2", "mock", "sqlalchemy_utils"], + extras_require={ + "dev": [ + "tox==3.7.0", # Should be kept in sync with tox.ini + "coveralls==1.7.0", + "pre-commit==1.14.4", + ], + "test": tests_require, + }, + tests_require=tests_require, ) diff --git a/tox.ini b/tox.ini new file mode 100644 index 00000000..b6b6ab1c --- /dev/null +++ b/tox.ini @@ -0,0 +1,20 @@ +[tox] +envlist = pre-commit,py{34,35,36,37}-sql{11,12,13} +skipsdist = true +minversion = 3.7.0 + +[testenv] +deps = + .[test] + sql11: sqlalchemy>=1.1,<1.2 + sql12: sqlalchemy>=1.2,<1.3 + sql13: sqlalchemy>=1.3,<1.4 +commands = + pytest graphene_sqlalchemy --cov=graphene_sqlalchemy {posargs} + +[testenv:pre-commit] +basepython=python3.7 +deps = + .[dev] +commands = + pre-commit {posargs:run --all-files} From 3106c6e6deeea134522f057b2bedb59ff0cc1137 Mon Sep 17 00:00:00 2001 From: Julien Nakache Date: Thu, 28 Mar 2019 20:56:53 -0400 Subject: [PATCH 2/2] revert lint --- .pre-commit-config.yaml | 18 +++++++++--------- examples/nameko_sqlalchemy/README.md | 2 +- examples/nameko_sqlalchemy/app.py | 2 +- examples/nameko_sqlalchemy/config.yml | 2 +- examples/nameko_sqlalchemy/requirements.txt | 2 +- examples/nameko_sqlalchemy/run.sh | 2 +- examples/nameko_sqlalchemy/service.py | 2 +- graphene_sqlalchemy/tests/test_converter.py | 3 ++- graphene_sqlalchemy/tests/test_query.py | 1 + graphene_sqlalchemy/tests/test_reflected.py | 1 + graphene_sqlalchemy/tests/test_schema.py | 1 + graphene_sqlalchemy/tests/test_types.py | 6 ++---- graphene_sqlalchemy/types.py | 8 +++----- 13 files changed, 25 insertions(+), 25 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9084d9c6..a710a5ff 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -7,12 +7,12 @@ repos: - id: check-merge-conflict - id: check-yaml - id: debug-statements - - id: end-of-file-fixer - exclude: ^docs/.*$ - # TODO Enable in its own PR - # - id: trailing-whitespace - # exclude: README.md -- repo: git://github.com/PyCQA/flake8 - rev: 88caf5ac484f5c09aedc02167c59c66ff0af0068 # 3.7.7 - hooks: - - id: flake8 +# TDOO Enable in separate PR +# - id: end-of-file-fixer +# exclude: ^docs/.*$ +# - id: trailing-whitespace +# exclude: README.md +# - repo: git://github.com/PyCQA/flake8 +# rev: 88caf5ac484f5c09aedc02167c59c66ff0af0068 # 3.7.7 +# hooks: +# - id: flake8 diff --git a/examples/nameko_sqlalchemy/README.md b/examples/nameko_sqlalchemy/README.md index 39cfe925..f60ccb00 100644 --- a/examples/nameko_sqlalchemy/README.md +++ b/examples/nameko_sqlalchemy/README.md @@ -51,4 +51,4 @@ Now the following command will setup the database, and start the server: Now head on over to postman and send POST request to: [http://127.0.0.1:5000/graphql](http://127.0.0.1:5000/graphql) -and run some queries! +and run some queries! \ No newline at end of file diff --git a/examples/nameko_sqlalchemy/app.py b/examples/nameko_sqlalchemy/app.py index 3080d56c..f7fe150a 100755 --- a/examples/nameko_sqlalchemy/app.py +++ b/examples/nameko_sqlalchemy/app.py @@ -33,4 +33,4 @@ def parse_body(self,request): elif content_type in ('application/x-www-form-urlencoded', 'multipart/form-data'): return request.form - return {} + return {} \ No newline at end of file diff --git a/examples/nameko_sqlalchemy/config.yml b/examples/nameko_sqlalchemy/config.yml index 8ca6a45c..cdea553c 100644 --- a/examples/nameko_sqlalchemy/config.yml +++ b/examples/nameko_sqlalchemy/config.yml @@ -1 +1 @@ -WEB_SERVER_ADDRESS: '0.0.0.0:5000' +WEB_SERVER_ADDRESS: '0.0.0.0:5000' \ No newline at end of file diff --git a/examples/nameko_sqlalchemy/requirements.txt b/examples/nameko_sqlalchemy/requirements.txt index be037f73..1f7be7db 100644 --- a/examples/nameko_sqlalchemy/requirements.txt +++ b/examples/nameko_sqlalchemy/requirements.txt @@ -1,4 +1,4 @@ graphene[sqlalchemy] SQLAlchemy==1.0.11 nameko -graphql-server-core +graphql-server-core \ No newline at end of file diff --git a/examples/nameko_sqlalchemy/run.sh b/examples/nameko_sqlalchemy/run.sh index bfe17d9a..d3953677 100755 --- a/examples/nameko_sqlalchemy/run.sh +++ b/examples/nameko_sqlalchemy/run.sh @@ -1,4 +1,4 @@ #!/bin/sh echo "Starting application service server" # Run Service -nameko run --config config.yml service +nameko run --config config.yml service \ No newline at end of file diff --git a/examples/nameko_sqlalchemy/service.py b/examples/nameko_sqlalchemy/service.py index 2188516f..394764e4 100644 --- a/examples/nameko_sqlalchemy/service.py +++ b/examples/nameko_sqlalchemy/service.py @@ -8,4 +8,4 @@ class DepartmentService: @http('POST', '/graphql') def query(self, request): - return App().query(request) + return App().query(request) \ No newline at end of file diff --git a/graphene_sqlalchemy/tests/test_converter.py b/graphene_sqlalchemy/tests/test_converter.py index c6c49bba..c2ec3e49 100644 --- a/graphene_sqlalchemy/tests/test_converter.py +++ b/graphene_sqlalchemy/tests/test_converter.py @@ -168,7 +168,7 @@ class Test(Base): ) graphene_type = convert_sqlalchemy_column(Test.column) - assert not graphene_type.kwargs["required"] + assert graphene_type.kwargs["required"] == False def test_should_scalar_list_convert_list(): @@ -343,3 +343,4 @@ def __init__(self, col1, col2): ) assert "Don't know how to convert the composite field" in str(excinfo.value) + diff --git a/graphene_sqlalchemy/tests/test_query.py b/graphene_sqlalchemy/tests/test_query.py index 8bb4891c..f1116d9d 100644 --- a/graphene_sqlalchemy/tests/test_query.py +++ b/graphene_sqlalchemy/tests/test_query.py @@ -555,3 +555,4 @@ def makeNodes(nodeList): assert set(node["node"]["name"] for node in value["edges"]) == set( node["node"]["name"] for node in expectedNoSort[key]["edges"] ) + diff --git a/graphene_sqlalchemy/tests/test_reflected.py b/graphene_sqlalchemy/tests/test_reflected.py index 46e10de9..c8a1a70f 100644 --- a/graphene_sqlalchemy/tests/test_reflected.py +++ b/graphene_sqlalchemy/tests/test_reflected.py @@ -18,3 +18,4 @@ def test_objecttype_registered(): assert issubclass(Reflected, ObjectType) assert Reflected._meta.model == ReflectedEditor assert list(Reflected._meta.fields.keys()) == ["editor_id", "name"] + diff --git a/graphene_sqlalchemy/tests/test_schema.py b/graphene_sqlalchemy/tests/test_schema.py index 628da185..909570a6 100644 --- a/graphene_sqlalchemy/tests/test_schema.py +++ b/graphene_sqlalchemy/tests/test_schema.py @@ -47,3 +47,4 @@ class Meta: model = Reporter only_fields = ("id", "email") assert list(Reporter2._meta.fields.keys()) == ["id", "email"] + diff --git a/graphene_sqlalchemy/tests/test_types.py b/graphene_sqlalchemy/tests/test_types.py index 183234e9..c11ec351 100644 --- a/graphene_sqlalchemy/tests/test_types.py +++ b/graphene_sqlalchemy/tests/test_types.py @@ -1,7 +1,7 @@ from collections import OrderedDict from graphene import Field, Int, Interface, ObjectType from graphene.relay import Node, is_node, Connection -import six # noqa: F401 +import six from promise import Promise from ..registry import Registry @@ -176,9 +176,7 @@ class TestConnection(Connection): class Meta: node = ReporterWithCustomOptions - def resolver(*args, **kwargs): - return Promise.resolve([]) - + resolver = lambda *args, **kwargs: Promise.resolve([]) result = SQLAlchemyConnectionField.connection_resolver( resolver, TestConnection, ReporterWithCustomOptions, None, None ) diff --git a/graphene_sqlalchemy/types.py b/graphene_sqlalchemy/types.py index 9c093e59..e8a05c8f 100644 --- a/graphene_sqlalchemy/types.py +++ b/graphene_sqlalchemy/types.py @@ -1,11 +1,9 @@ from collections import OrderedDict -import sqlalchemy from sqlalchemy.inspection import inspect as sqlalchemyinspect from sqlalchemy.ext.hybrid import hybrid_property from sqlalchemy.orm.exc import NoResultFound -import graphene from graphene import Field # , annotate, ResolveInfo from graphene.relay import Connection, Node from graphene.types.objecttype import ObjectType, ObjectTypeOptions @@ -82,9 +80,9 @@ def construct_fields(model, registry, only_fields, exclude_fields): class SQLAlchemyObjectTypeOptions(ObjectTypeOptions): - model = None # type: sqlalchemy.Model - registry = None # type: sqlalchemy.Registry - connection = None # type: graphene.Type[Connection] + model = None # type: Model + registry = None # type: Registry + connection = None # type: Type[Connection] id = None # type: str