diff --git a/.bazelrc b/.bazelrc
index 266fbd38..2777f4cf 100644
--- a/.bazelrc
+++ b/.bazelrc
@@ -1,3 +1,22 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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 --incompatible_strict_action_env
run --incompatible_strict_action_env
test --incompatible_strict_action_env
diff --git a/.circleci/config.yml b/.circleci/config.yml
deleted file mode 100644
index efe57ebc..00000000
--- a/.circleci/config.yml
+++ /dev/null
@@ -1,295 +0,0 @@
-#
-# Copyright (C) 2020 Grakn Labs
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as
-# published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see .
-#
-
-version: 2.1
-commands:
- install-bazel:
- steps:
- - run: curl -OL https://raw.githubusercontent.com/graknlabs/dependencies/master/tool/bazelinstall/linux.sh
- - run: bash ./linux.sh && rm ./linux.sh
- - run: curl -OL https://raw.githubusercontent.com/graknlabs/dependencies/master/tool/bazelinstall/rbe.sh
- - run: bash ./rbe.sh && rm ./rbe.sh
-
- run-bazel:
- parameters:
- command:
- type: string
- steps:
- - run: bazel run @graknlabs_dependencies//tool/bazelrun:rbe -- << parameters.command >>
-
- install-artifact-credentials:
- steps:
- - run: |
- ARTIFACT_USERNAME=$REPO_GRAKN_USERNAME \
- ARTIFACT_PASSWORD=$REPO_GRAKN_PASSWORD \
- bazel run @graknlabs_dependencies//distribution/artifact:create-netrc
-
- run-grakn:
- steps:
- - run-bazel:
- command: bazel run //:grakn-extractor -- dist/grakn-core-server-linux
- - run: nohup ./dist/grakn-core-server-linux/grakn server start
-
-jobs:
- build:
- machine:
- image: ubuntu-1604:201903-01
- working_directory: ~/client-python
- steps:
- - checkout
- - install-bazel
- - install-artifact-credentials
- - run-bazel:
- command: bazel build //...
-
- test-concept:
- machine:
- image: ubuntu-1604:201903-01
- working_directory: ~/client-python
- steps:
- - checkout
- - install-bazel
- - install-artifact-credentials
- - run-bazel:
- command: bazel test //:test_concept --test_output=errors
-
- test-grakn:
- machine:
- image: ubuntu-1604:201903-01
- working_directory: ~/client-python
- steps:
- - checkout
- - install-bazel
- - install-artifact-credentials
- - run-bazel:
- command: bazel test //:test_grakn --test_output=errors
-
- test-keyspace:
- machine:
- image: ubuntu-1604:201903-01
- working_directory: ~/client-python
- steps:
- - checkout
- - install-bazel
- - install-artifact-credentials
- - run-bazel:
- command: bazel test //:test_keyspace --test_output=errors
-
- test-answer:
- machine:
- image: ubuntu-1604:201903-01
- working_directory: ~/client-python
- steps:
- - checkout
- - install-bazel
- - install-artifact-credentials
- - run-bazel:
- command: bazel test //:test_answer --test_output=errors
-
- deploy-pip-snapshot:
- machine:
- image: ubuntu-1604:201903-01
- working_directory: ~/client-python
- steps:
- - checkout
- - install-bazel
- - run: |
- export DEPLOY_PIP_USERNAME=$REPO_GRAKN_USERNAME
- export DEPLOY_PIP_PASSWORD=$REPO_GRAKN_PASSWORD
- bazel run --define version=$(git rev-parse HEAD) //:deploy-pip -- snapshot
-
- test-deployment:
- machine:
- image: ubuntu-1604:201903-01
- working_directory: ~/client-python
- steps:
- - checkout
- - install-bazel
- - install-artifact-credentials
- - run: sudo apt-get update
- - run: sudo apt-get install python-pip
- - run-grakn
- - run: sleep 60
- - run:
- name: Run test-deployment for client-python
- command: |
- echo -n "0.0.0-$CIRCLE_SHA1" > VERSION
- sed -i -e "s/CLIENT_PYTHON_VERSION_MARKER/$(cat VERSION)/g" tests/deployment/requirements.txt
- cat tests/deployment/requirements.txt
- pip install --upgrade pip
- pip install -r tests/deployment/requirements.txt
- cd tests/deployment/ && python -m unittest test
-
- sync-dependencies-snapshot:
- machine:
- image: ubuntu-1604:201903-01
- working_directory: ~/client-python
- steps:
- - install-bazel
- - checkout
- - run: |
- export SYNC_DEPENDENCIES_TOKEN=$REPO_GITHUB_TOKEN
- bazel run @graknlabs_dependencies//tool/sync:dependencies -- \
- --source client-python@$CIRCLE_SHA1 \
- --targets grakn-kgms:master kglib:master
-
- release-approval:
- machine:
- image: ubuntu-1604:201903-01
- working_directory: ~/client-python
- steps:
- - install-bazel
- - checkout
- - run: |
- export RELEASE_APPROVAL_USERNAME=$REPO_GITHUB_USERNAME
- export RELEASE_APPROVAL_TOKEN=$REPO_GITHUB_TOKEN
- bazel run @graknlabs_dependencies//tool/release:approval
-
- release-validate:
- machine:
- image: ubuntu-1604:201903-01
- steps:
- - install-bazel
- - checkout
- - run: |
- bazel test //:release-validate-deps --test_output=streamed
-
- deploy-github:
- machine:
- image: ubuntu-1604:201903-01
- working_directory: ~/client-python
- steps:
- - install-bazel
- - checkout
- - run: |
- pip install certifi
- export RELEASE_NOTES_TOKEN=$REPO_GITHUB_TOKEN
- bazel run @graknlabs_dependencies//tool/release:create-notes -- client-python $(cat VERSION) ./RELEASE_TEMPLATE.md
- - run: bazel clean --expunge
- - run: |
- export DEPLOY_GITHUB_TOKEN=$REPO_GITHUB_TOKEN
- bazel run --define version=$(cat VERSION) //:deploy-github -- $CIRCLE_SHA1
-
- deploy-pip-release:
- machine:
- image: ubuntu-1604:201903-01
- working_directory: ~/client-python
- steps:
- - install-bazel
- - checkout
- - run: |
- export DEPLOY_PIP_USERNAME=$REPO_PYPI_USERNAME
- export DEPLOY_PIP_PASSWORD=$REPO_PYPI_PASSWORD
- bazel run --define version=$(cat VERSION) //:deploy-pip -- release
-
- sync-dependencies-release:
- machine:
- image: ubuntu-1604:201903-01
- working_directory: ~/client-python
- steps:
- - install-bazel
- - checkout
- - run: |
- export SYNC_DEPENDENCIES_TOKEN=$REPO_GITHUB_TOKEN
- bazel run @graknlabs_dependencies//tool/sync:dependencies -- \
- --source client-python@$(cat VERSION) \
- --targets grakn-kgms:master docs:master examples:master kglib:master
-
- release-cleanup:
- machine:
- image: ubuntu-1604:201903-01
- steps:
- - checkout
- - run: git push --delete https://$REPO_GITHUB_TOKEN@github.com/graknlabs/client-python $CIRCLE_BRANCH
-
-workflows:
- client-python:
- jobs:
- - build:
- filters:
- branches:
- ignore: client-python-release-branch
- - test-concept:
- filters:
- branches:
- ignore: client-python-release-branch
- - test-grakn:
- filters:
- branches:
- ignore: client-python-release-branch
- - test-keyspace:
- filters:
- branches:
- ignore: client-python-release-branch
- - test-answer:
- filters:
- branches:
- ignore: client-python-release-branch
- - deploy-pip-snapshot:
- filters:
- branches:
- only: master
- requires:
- - build
- - test-concept
- - test-grakn
- - test-keyspace
- - test-answer
- - test-deployment:
- filters:
- branches:
- only: master
- requires:
- - deploy-pip-snapshot
- - release-approval:
- filters:
- branches:
- only: master
- requires:
- - test-deployment
-
- client-python-release:
- jobs:
- - release-validate:
- filters:
- branches:
- only: client-python-release-branch
- - deploy-github:
- filters:
- branches:
- only: client-python-release-branch
- requires:
- - release-validate
- - deploy-approval:
- type: approval
- filters:
- branches:
- only: client-python-release-branch
- requires:
- - deploy-github
- - deploy-pip-release:
- filters:
- branches:
- only: client-python-release-branch
- requires:
- - deploy-approval
- - release-cleanup:
- filters:
- branches:
- only: client-python-release-branch
- requires:
- - deploy-pip-release
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 4f666c5f..7b8b1728 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,84 @@
-.ijwb
-.idea
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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.
+#
+
+# Mac files #
+.DS_Store
+
+# Bazel files #
bazel-*
+
+# IDE files #
+.idea/
+*.iml
+*.geany
+.ijwb/
+
+# VS Code files #
+.vscode/
+.settings/
+.project
+.classpath
+
+# Compiled files #
+dist/
+out/
+*.class
+
+# Mobile Tools for Java (J2ME) files #
+.mtj.tmp/
+
+# Package Files #
+*.jarg
+*.war
+*.ear
+
+# Debug files #
+dep.tree
+
+# Databases #
+db/
+
+# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml #
+hs_err_pid*
+
+# Log files #
+logs/
+*.log
+
+# Analytics files #
+grakn-test/db/
+grakn-test/output/
+grakn-test/*.txt
+
+# Sample Date Files #
+grakn-test/test-biomed/data/*
+
+# Benchmark Files #
+grakn-test/test-integration/benchmarks/
+
+# VIM swap files #
+*.swp
+*.swo
+
+# Python cache #
__pycache__
+
+# Other #
**.pyc
-venv
\ No newline at end of file
+venv
diff --git a/.grabl/automation.yml b/.grabl/automation.yml
index 515c82a5..b32a34d9 100644
--- a/.grabl/automation.yml
+++ b/.grabl/automation.yml
@@ -1,7 +1,37 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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.
+#
+
config:
version-candidate: VERSION
+ dependencies:
+ dependencies: [build]
+ common: [build, release]
build:
+ quality:
+ filter:
+ owner: graknlabs
+ branch: master
+ dependency-analysis:
+ machine: graknlabs-ubuntu-20.04
+ script: |
+ bazel run @graknlabs_dependencies//grabl/analysis:dependency-analysis
correctness:
build:
machine: graknlabs-ubuntu-20.04
@@ -15,47 +45,51 @@ build:
ARTIFACT_PASSWORD=$REPO_GRAKN_PASSWORD \
bazel run @graknlabs_dependencies//distribution/artifact:create-netrc
bazel build //...
- test-concept:
- machine: graknlabs-ubuntu-20.04
- type: foreground
- script: |
- pyenv global 3.6.10
- sudo unlink /usr/bin/python3
- sudo ln -s $(which python3) /usr/bin/python3
- sudo ln -s /usr/share/pyshared/lsb_release.py /opt/pyenv/versions/3.6.10/lib/python3.6/site-packages/lsb_release.py
- ARTIFACT_USERNAME=$REPO_GRAKN_USERNAME \
- ARTIFACT_PASSWORD=$REPO_GRAKN_PASSWORD \
- bazel run @graknlabs_dependencies//distribution/artifact:create-netrc
- bazel test //:test_concept --test_output=errors
- test-keyspace:
- machine: graknlabs-ubuntu-20.04
- type: foreground
- script: |
- pyenv global 3.6.10
- sudo unlink /usr/bin/python3
- sudo ln -s $(which python3) /usr/bin/python3
- sudo ln -s /usr/share/pyshared/lsb_release.py /opt/pyenv/versions/3.6.10/lib/python3.6/site-packages/lsb_release.py
- ARTIFACT_USERNAME=$REPO_GRAKN_USERNAME \
- ARTIFACT_PASSWORD=$REPO_GRAKN_PASSWORD \
- bazel run @graknlabs_dependencies//distribution/artifact:create-netrc
- bazel test //:test_keyspace --test_output=errors
- test-answer:
- machine: graknlabs-ubuntu-20.04
- type: foreground
- script: |
- pyenv global 3.6.10
- sudo unlink /usr/bin/python3
- sudo ln -s $(which python3) /usr/bin/python3
- sudo ln -s /usr/share/pyshared/lsb_release.py /opt/pyenv/versions/3.6.10/lib/python3.6/site-packages/lsb_release.py
- ARTIFACT_USERNAME=$REPO_GRAKN_USERNAME \
- ARTIFACT_PASSWORD=$REPO_GRAKN_PASSWORD \
- bazel run @graknlabs_dependencies//distribution/artifact:create-netrc
- bazel test //:test_answer --test_output=errors
+ bazel run @graknlabs_dependencies//tool/checkstyle:test-coverage
+ bazel test $(bazel query 'kind(checkstyle_test, //...)')
+# test-concept:
+# machine: graknlabs-ubuntu-20.04
+# type: foreground
+# script: |
+# pyenv global 3.6.10
+# sudo unlink /usr/bin/python3
+# sudo ln -s $(which python3) /usr/bin/python3
+# sudo ln -s /usr/share/pyshared/lsb_release.py /opt/pyenv/versions/3.6.10/lib/python3.6/site-packages/lsb_release.py
+# ARTIFACT_USERNAME=$REPO_GRAKN_USERNAME \
+# ARTIFACT_PASSWORD=$REPO_GRAKN_PASSWORD \
+# bazel run @graknlabs_dependencies//distribution/artifact:create-netrc
+# bazel test //:test_concept --test_output=errors
+# test-connection:
+# machine: graknlabs-ubuntu-20.04
+# type: foreground
+# script: |
+# pyenv global 3.6.10
+# sudo unlink /usr/bin/python3
+# sudo ln -s $(which python3) /usr/bin/python3
+# sudo ln -s /usr/share/pyshared/lsb_release.py /opt/pyenv/versions/3.6.10/lib/python3.6/site-packages/lsb_release.py
+# ARTIFACT_USERNAME=$REPO_GRAKN_USERNAME \
+# ARTIFACT_PASSWORD=$REPO_GRAKN_PASSWORD \
+# bazel run @graknlabs_dependencies//distribution/artifact:create-netrc
+# bazel test //:test_connection --test_output=errors
+# test-query:
+# machine: graknlabs-ubuntu-20.04
+# type: foreground
+# script: |
+# pyenv global 3.6.10
+# sudo unlink /usr/bin/python3
+# sudo ln -s $(which python3) /usr/bin/python3
+# sudo ln -s /usr/share/pyshared/lsb_release.py /opt/pyenv/versions/3.6.10/lib/python3.6/site-packages/lsb_release.py
+# ARTIFACT_USERNAME=$REPO_GRAKN_USERNAME \
+# ARTIFACT_PASSWORD=$REPO_GRAKN_PASSWORD \
+# bazel run @graknlabs_dependencies//distribution/artifact:create-netrc
+# bazel test //:test_query --test_output=errors
deploy-pip-snapshot:
+ machine: graknlabs-ubuntu-20.04
+ # TODO: should depend on tests
+ dependencies: [build]
filter:
owner: graknlabs
branch: master
- machine: graknlabs-ubuntu-20.04
type: foreground
script: |
pyenv global 3.6.10
@@ -69,12 +103,12 @@ build:
export DEPLOY_PIP_PASSWORD=$REPO_GRAKN_PASSWORD
bazel run --define version=$(git rev-parse HEAD) //:deploy-pip -- snapshot
test-deployment:
+ machine: graknlabs-ubuntu-20.04
+ dependencies: [deploy-pip-snapshot]
filter:
owner: graknlabs
branch: master
- machine: graknlabs-ubuntu-20.04
type: foreground
- dependencies: [deploy-pip-snapshot]
script: |
pyenv global 3.6.10
sudo unlink /usr/bin/python3
@@ -86,29 +120,22 @@ build:
bazel run //:grakn-extractor -- dist/grakn-core-server-linux
nohup ./dist/grakn-core-server-linux/grakn server start
sleep 60
- echo -n "0.0.0-$CIRCLE_SHA1" > VERSION
- sed -i -e "s/CLIENT_PYTHON_VERSION_MARKER/$(cat VERSION)/g" tests/deployment/requirements.txt
- cat tests/deployment/requirements.txt
+ echo -n "0.0.0-$GRABL_COMMIT" > VERSION
+ sed -i -e "s/CLIENT_PYTHON_VERSION_MARKER/$(cat VERSION)/g" test/deployment/requirements.txt
+ cat test/deployment/requirements.txt
pip install --upgrade pip
- pip install -r tests/deployment/requirements.txt
- cd tests/deployment/ && python -m unittest test
- execution:
- - build
- - test-concept
- - test-keyspace
- - test-answer
- - deploy-pip-snapshot
- - test-deployment
-
+ pip install -r test/deployment/requirements.txt
+ cd test/deployment/ && python -m unittest test
release:
filter:
owner: graknlabs
branch: master
- validation:
- validate-dependencies:
- machine: graknlabs-ubuntu-20.04
- script: bazel test //:release-validate-deps --test_output=streamed
+ # TODO: add it back once we're able to depend on @graknlabs_protocol as bazel rather than artifact dependency
+ # validation:
+ # validate-dependencies:
+ # machine: graknlabs-ubuntu-20.04
+ # script: bazel test //:release-validate-deps --test_output=streamed
deployment:
deploy-github:
machine: graknlabs-ubuntu-20.04
@@ -122,7 +149,7 @@ release:
bazel run @graknlabs_dependencies//tool/release:create-notes -- client-python $(cat VERSION) ./RELEASE_TEMPLATE.md
bazel clean --expunge
export DEPLOY_GITHUB_TOKEN=$REPO_GITHUB_TOKEN
- bazel run --define version=$(cat VERSION) //:deploy-github -- $CIRCLE_SHA1
+ bazel run --define version=$(cat VERSION) //:deploy-github -- $GRABL_COMMIT
deploy-pip-release:
machine: graknlabs-ubuntu-20.04
script: |
diff --git a/BUILD b/BUILD
index 5657fc16..3d734b50 100644
--- a/BUILD
+++ b/BUILD
@@ -1,23 +1,24 @@
#
-# Copyright (C) 2020 Grakn Labs
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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
#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as
-# published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
+# http://www.apache.org/licenses/LICENSE-2.0
#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see .
+# 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.
#
exports_files(["requirements.txt", "deployment.bzl", "RELEASE_TEMPLATE.md"])
-load("@rules_python//python:defs.bzl", "py_library", "py_test")
load("@graknlabs_client_python_pip//:requirements.bzl",
graknlabs_client_python_requirement = "requirement")
@@ -27,6 +28,7 @@ load("@graknlabs_bazel_distribution//github:rules.bzl", "deploy_github")
load("@graknlabs_bazel_distribution//artifact:rules.bzl", "artifact_extractor")
load("@graknlabs_dependencies//tool/release:rules.bzl", "release_validate_deps")
+load("@graknlabs_dependencies//tool/checkstyle:rules.bzl", "checkstyle_test")
load("@graknlabs_dependencies//distribution:deployment.bzl", "deployment")
load(":deployment.bzl", github_deployment = "deployment")
@@ -36,7 +38,7 @@ py_library(
name = "client_python",
srcs = glob(["grakn/**/*.py"]),
deps = [
- "@graknlabs_protocol//grpc/python:protocol",
+ graknlabs_client_python_requirement("graknprotocol"),
graknlabs_client_python_requirement("protobuf"),
graknlabs_client_python_requirement("grpcio"),
graknlabs_client_python_requirement("six"),
@@ -44,6 +46,17 @@ py_library(
visibility =["//visibility:public"]
)
+checkstyle_test(
+ name = "checkstyle",
+ include = glob([
+ "*",
+ ".grabl/automation.yml",
+ "grakn/**/*",
+ ]),
+ license_type = "apache",
+ size = "small",
+)
+
assemble_pip(
name = "assemble-pip",
target = ":client_python",
@@ -66,7 +79,12 @@ assemble_pip(
author = "Grakn Labs",
author_email = "community@grakn.ai",
license = "Apache-2.0",
- install_requires=['grpcio==1.24.1,<2', 'protobuf==3.6.1', 'six>=1.11.0'],
+ install_requires=[
+ 'graknprotocol==0.0.0-6bf8c601ecd57a2869cde56c17eec8784a9a2804',
+ 'grpcio==1.33.2',
+ 'protobuf==3.6.1',
+ 'six>=1.11.0',
+ ],
keywords = ["grakn", "database", "graph", "knowledgebase", "knowledge-engineering"],
description = "Grakn Client for Python",
long_description_file = "//:README.md",
@@ -90,86 +108,20 @@ deploy_github(
repository = github_deployment["github.repository"],
)
-py_test(
- name = "test_concept",
- srcs = [
- "tests/integration/base.py",
- "tests/integration/test_concept.py"
- ],
- deps = [
- ":client_python",
- ],
- data = ["@graknlabs_grakn_core_artifact//file"],
- args = ["$(location @graknlabs_grakn_core_artifact//file)"],
- python_version = "PY3"
-)
-
-py_test(
- name = "test_grakn",
- srcs = [
- "tests/integration/base.py",
- "tests/integration/test_grakn.py"
- ],
- deps = [
- ":client_python",
- ],
- data = ["@graknlabs_grakn_core_artifact//file"],
- args = ["$(location @graknlabs_grakn_core_artifact//file)"],
- python_version = "PY3"
-)
-
-py_test(
- name = "test_keyspace",
- srcs = [
- "tests/integration/base.py",
- "tests/integration/test_keyspace.py"
- ],
- deps = [
- ":client_python",
- ],
- data = ["@graknlabs_grakn_core_artifact//file"],
- args = ["$(location @graknlabs_grakn_core_artifact//file)"],
- python_version = "PY3"
-)
-
-py_test(
- name = "test_answer",
- srcs = [
- "tests/integration/base.py",
- "tests/integration/test_answer.py"
- ],
- deps = [
- ":client_python",
- ],
- size = "large",
- data = ["@graknlabs_grakn_core_artifact//file"],
- args = ["$(location @graknlabs_grakn_core_artifact//file)"],
- python_version = "PY3"
-)
-
-test_suite(
- name = "test_integration",
- tests = [
- ":test_concept",
- ":test_grakn",
- ":test_keyspace",
- ":test_answer",
- ]
-)
-
artifact_extractor(
name = "grakn-extractor",
- artifact = "@graknlabs_grakn_core_artifact//file",
+ artifact = "@graknlabs_grakn_core_artifact_linux//file",
)
-release_validate_deps(
- name = "release-validate-deps",
- refs = "@graknlabs_client_python_workspace_refs//:refs.json",
- tagged_deps = [
- "@graknlabs_protocol",
- ],
- tags = ["manual"] # in order for bazel test //... to not fail
-)
+# TODO: add it back once we're able to depend on @graknlabs_protocol as bazel rather than artifact dependency
+#release_validate_deps(
+# name = "release-validate-deps",
+# refs = "@graknlabs_client_python_workspace_refs//:refs.json",
+# tagged_deps = [
+# "@graknlabs_protocol",
+# ],
+# tags = ["manual"] # in order for bazel test //... to not fail
+#)
# CI targets that are not declared in any BUILD file, but are called externally
filegroup(
diff --git a/WORKSPACE b/WORKSPACE
index 49058f6a..32bf782e 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -1,18 +1,20 @@
#
-# Copyright (C) 2020 Grakn Labs
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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
#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as
-# published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
+# http://www.apache.org/licenses/LICENSE-2.0
#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see .
+# 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.
#
workspace(name = "graknlabs_client_python")
@@ -59,6 +61,10 @@ graknlabs_dependencies_ci_pip()
load("@graknlabs_dependencies_ci_pip//:requirements.bzl", graknlabs_dependencies_pip_install = "pip_install")
graknlabs_dependencies_pip_install()
+# Load //tool/checkstyle
+load("@graknlabs_dependencies//tool/checkstyle:deps.bzl", checkstyle_deps = "deps")
+checkstyle_deps()
+
######################################
# Load @graknlabs_bazel_distribution #
######################################
@@ -82,19 +88,16 @@ graknlabs_bazel_distribution_pip_install()
load("@graknlabs_bazel_distribution//github:deps.bzl", github_deps = "deps")
github_deps()
-############################
-# Load @graknlabs_protocol #
-############################
-
-load("//dependencies/graknlabs:repositories.bzl", "graknlabs_protocol")
-graknlabs_protocol()
+################################
+# Load @graknlabs dependencies #
+################################
-#######################################
-# Load @graknlabs_grakn_core_artifact #
-#######################################
+load("//dependencies/graknlabs:repositories.bzl", "graknlabs_common")
+graknlabs_common()
-load("//dependencies/graknlabs:artifacts.bzl", "graknlabs_grakn_core_artifact")
-graknlabs_grakn_core_artifact()
+# Load artifacts
+load("//dependencies/graknlabs:artifacts.bzl", "graknlabs_grakn_core_artifacts")
+graknlabs_grakn_core_artifacts()
#################################
# Load @graknlabs_client_python #
diff --git a/dependencies/graknlabs/BUILD b/dependencies/graknlabs/BUILD
index fb0409e3..4162fab4 100644
--- a/dependencies/graknlabs/BUILD
+++ b/dependencies/graknlabs/BUILD
@@ -1,16 +1,26 @@
#
-# Copyright (C) 2020 Grakn Labs
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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
#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as
-# published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
+# http://www.apache.org/licenses/LICENSE-2.0
#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see .
+# 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.
#
+
+load("@graknlabs_dependencies//tool/checkstyle:rules.bzl", "checkstyle_test")
+
+checkstyle_test(
+ name = "checkstyle",
+ include = glob(["*"]),
+ license_type = "apache",
+)
diff --git a/dependencies/graknlabs/artifacts.bzl b/dependencies/graknlabs/artifacts.bzl
index 0ab4be1c..6d3f1e37 100644
--- a/dependencies/graknlabs/artifacts.bzl
+++ b/dependencies/graknlabs/artifacts.bzl
@@ -1,30 +1,31 @@
#
-# Copyright (C) 2020 Grakn Labs
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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
#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as
-# published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
+# http://www.apache.org/licenses/LICENSE-2.0
#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see .
+# 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.
#
-load("@graknlabs_bazel_distribution//artifact:rules.bzl", "artifact_file")
+load("@graknlabs_dependencies//distribution/artifact:rules.bzl", "native_artifact_files")
load("@graknlabs_dependencies//distribution:deployment.bzl", "deployment")
-def graknlabs_grakn_core_artifact():
- artifact_file(
+def graknlabs_grakn_core_artifacts():
+ native_artifact_files(
name = "graknlabs_grakn_core_artifact",
group_name = "graknlabs_grakn_core",
- artifact_name = "grakn-core-all-linux-{version}.tar.gz",
- commit_source = deployment["artifact.snapshot"],
+ artifact_name = "grakn-core-server-{platform}-{version}.tar.gz",
tag_source = deployment["artifact.release"],
- # TODO - client-python is broken with 1.8.1, as current deps (eg. protocol) are preparing for Grakn 2.0
- tag = "1.8.1",
+ commit_source = deployment["artifact.snapshot"],
+ commit = "8e80542cd6afe3318859320565e8afe119e7ae11",
)
diff --git a/dependencies/graknlabs/repositories.bzl b/dependencies/graknlabs/repositories.bzl
index 3457ecd0..156543bb 100644
--- a/dependencies/graknlabs/repositories.bzl
+++ b/dependencies/graknlabs/repositories.bzl
@@ -1,18 +1,20 @@
#
-# Copyright (C) 2020 Grakn Labs
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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
#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as
-# published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
+# http://www.apache.org/licenses/LICENSE-2.0
#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see .
+# 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.
#
load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
@@ -21,12 +23,12 @@ def graknlabs_dependencies():
git_repository(
name = "graknlabs_dependencies",
remote = "https://github.com/graknlabs/dependencies",
- commit = "1c86421327bec68a83c3f88d728add07010f797a", # sync-marker: do not remove this comment, this is used for sync-dependencies by @graknlabs_dependencies
+ commit = "ed2c074bea897dada26e4f112c1d08f739e90012", # sync-marker: do not remove this comment, this is used for sync-dependencies by @graknlabs_dependencies
)
-def graknlabs_protocol():
+def graknlabs_common():
git_repository(
- name = "graknlabs_protocol",
- remote = "https://github.com/graknlabs/protocol",
- tag = "1.0.7", # sync-marker: do not remove this comment, this is used for sync-dependencies by @graknlabs_protocol
+ name = "graknlabs_common",
+ remote = "https://github.com/graknlabs/common",
+ commit = "cfd261fd5412a0b45cb5494555e4491dd3ce5f64" # sync-marker: do not remove this comment, this is used for sync-dependencies by @graknlabs_common
)
diff --git a/deployment.bzl b/deployment.bzl
index c41b5eb4..098c1de2 100644
--- a/deployment.bzl
+++ b/deployment.bzl
@@ -1,18 +1,20 @@
#
-# Copyright (C) 2020 Grakn Labs
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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
#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as
-# published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
+# http://www.apache.org/licenses/LICENSE-2.0
#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see .
+# 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.
#
deployment = {
diff --git a/grakn/client.py b/grakn/client.py
index 6ca2642d..479b0bf1 100644
--- a/grakn/client.py
+++ b/grakn/client.py
@@ -18,33 +18,31 @@
#
import grpc
+import sched
+import time
-from grakn.service.Session.util.enums import ValueType # user-facing ValueType enum
+from grakn.options import GraknOptions
+from grakn.rpc.database_manager import DatabaseManager as _DatabaseManager
+from grakn.rpc.session import Session as _Session, SessionType
-from grakn.service.Session.util.RequestBuilder import RequestBuilder, QueryOptions
-from grakn.service.Session.util.enums import TxType as _TxType
-from grakn.service.Keyspace.KeyspaceService import KeyspaceService
-from grakn.service.Session.TransactionService import TransactionService
-from grakn_protocol.session.Session_pb2_grpc import SessionServiceStub
-from grakn.exception.GraknError import GraknError
+# Repackaging these enums allows users to import everything they (most likely) need from "grakn.client"
+from grakn.rpc.transaction import TransactionType # noqa # pylint: disable=unused-import
+from grakn.concept.type.attribute_type import ValueType # noqa # pylint: disable=unused-import
class GraknClient(object):
- """ A client/representation of a Grakn instance"""
+ DEFAULT_URI = "localhost:1729"
- def __init__(self, uri, credentials=None):
- self.uri = uri
- self.credentials = credentials
- self._channel = grpc.insecure_channel(uri)
- self._keyspace_service = KeyspaceService(self.uri, self._channel, credentials)
+ def __init__(self, address=DEFAULT_URI):
+ self._channel = grpc.insecure_channel(address)
+ self._databases = _DatabaseManager(self._channel)
+ self._scheduler = sched.scheduler(time.time, time.sleep)
- def session(self, keyspace):
- """ Open a session for a specific keyspace. Can be used as `with Grakn('localhost:48555').session(keyspace='test') as session: ... ` or as normal assignment"""
- return Session(self.uri, keyspace, self._channel, self.credentials)
- session.__annotations__ = {'keyspace': str}
+ def session(self, database: str, session_type: SessionType, options=GraknOptions()):
+ return _Session(self, database, session_type, options)
- def keyspaces(self):
- return self._keyspace_service
+ def databases(self):
+ return self._databases
def close(self):
self._channel.close()
@@ -52,165 +50,9 @@ def close(self):
def __enter__(self):
return self
- def __exit__(self, type, value, tb):
+ def __exit__(self, exc_type, exc_val, exc_tb):
self.close()
- if tb is None:
- # No exception
+ if exc_tb is None:
pass
else:
- #print("Closing Client due to exception: {0} \n traceback: \n {1}".format(type, tb))
return False
-
-
-class Session(object):
- """ A session for a Grakn instance and a specific keyspace """
-
- def __init__(self, uri, keyspace, channel, credentials):
-
- if not isinstance(uri, str):
- raise TypeError('expected string for uri')
-
- if not isinstance(keyspace, str):
- raise TypeError('expected string for keyspace')
-
- self.keyspace = keyspace
- self.uri = uri
- self.credentials = credentials
-
- self._stub = SessionServiceStub(channel)
- self._closed = False
-
- try:
- open_session_response = self._stub.open(RequestBuilder.open_session(keyspace, self.credentials))
- self.session_id = open_session_response.sessionId
- except Exception as e:
- raise GraknError('Could not obtain sessionId for keyspace "{0}", stems from: {1}'.format(keyspace, e))
-
- __init__.__annotations__ = {'uri': str, 'keyspace': str}
-
- def transaction(self):
- """ Build a read or write transaction to Grakn on this keyspace (ie. session.transaction().read() or .write()) """
- if self._closed:
- raise GraknError("Session is closed")
-
- # create a transaction service which hides GRPC usage
- return TransactionBuilder(self.session_id, self._stub.transaction)
-
- def close(self):
- """ Close this keyspace session """
- close_session_req = RequestBuilder.close_session(self.session_id)
- self._stub.close(close_session_req)
- self._closed = True
-
- def __enter__(self):
- return self
-
- def __exit__(self, type, value, tb):
- self.close()
- if tb is None:
- # No exception
- pass
- else:
- #print("Closing Session due to exception: {0} \n traceback: \n {1}".format(type, tb))
- return False
-
-
-class TransactionBuilder(object):
- def __init__(self, session_id, transaction_rpc_constructor):
- self._session_id = session_id
- self._transaction_rpc_constructor = transaction_rpc_constructor
-
- def read(self):
- transaction_service = TransactionService(self._session_id, _TxType.READ, self._transaction_rpc_constructor)
- return Transaction(transaction_service)
-
- def write(self):
- transaction_service = TransactionService(self._session_id, _TxType.WRITE, self._transaction_rpc_constructor)
- return Transaction(transaction_service)
-
-
-class Transaction(object):
- """ Presents the Grakn interface to the user, actual work with GRPC happens in TransactionService """
-
- Options = QueryOptions
-
- def __init__(self, transaction_service):
- self._tx_service = transaction_service
- __init__.__annotations__ = {'transaction_service': TransactionService}
-
- def __enter__(self):
- return self
-
- def __exit__(self, type, value, tb):
- self.close()
- if tb is None:
- # No exception
- pass
- else:
- #print("Closing Transaction due to exception: {0} \n traceback: \n {1}".format(type, tb))
- return False
-
- def query(self, query, infer=Options.SERVER_DEFAULT, explain=Options.SERVER_DEFAULT, batch_size=Options.SERVER_DEFAULT):
- """ Execute a Graql query with query options"""
- return self._tx_service.query(query, infer, explain, batch_size)
- query.__annotations__ = {'query': str}
-
- def commit(self):
- """ Commit and close this transaction, persisting changes to Grakn """
- self._tx_service.commit()
- self.close()
-
- def close(self):
- """ Close this transaction without committing """
- self._tx_service.close() # close the service
-
- def is_open(self):
- """ Check if this transaction is open"""
- return not self._tx_service.is_closed()
-
- def get_concept(self, concept_id):
- """ Retrieve a concept by Concept ID (string) """
- return self._tx_service.get_concept(concept_id)
- get_concept.__annotations__ = {'concept_id': str}
-
- def get_schema_concept(self, label):
- """ Retrieve a schema concept by its label (eg. those defined using `define` or tx.put...() """
- return self._tx_service.get_schema_concept(label)
- get_schema_concept.__annotations__ = {'label': str}
-
- def get_attributes_by_value(self, attribute_value, value_type):
- """ Retrieve atttributes with a specific value and value type
-
- :param any attribute_value: the value to match
- :param grakn.ValueType value_type: The value type of the value in Grakn, as given by the grakn.ValueType enum
- """
- return self._tx_service.get_attributes_by_value(attribute_value, value_type)
-
- def put_entity_type(self, label):
- """ Define a new entity type with the given label """
- return self._tx_service.put_entity_type(label)
- put_entity_type.__annotations__ = {'label': str}
-
- def put_relation_type(self, label):
- """ Define a new relation type with the given label """
- return self._tx_service.put_relation_type(label)
- put_relation_type.__annotations__ = {'label': str}
-
- def put_attribute_type(self, label, value_type):
- """ Define a new attribute type with the given label and value type
-
- :param str label: the label of the attribute type
- :param grakn.ValueType value_type: the data type of the value to be stored, as given by the grakn.ValueType enum
- """
- return self._tx_service.put_attribute_type(label, value_type)
- put_attribute_type.__annotations__ = {'label': str}
-
- def put_role(self, label):
- """ Define a role with the given label """
- return self._tx_service.put_role(label)
- put_role.__annotations__ = {'label': str}
-
- def put_rule(self, label, when, then):
- """ Define a new rule with the given label, when and then clauses """
- return self._tx_service.put_rule(label, when, then)
- put_rule.__annotations__ = {'label': str, 'when': str, 'then': str}
diff --git a/grakn/exception/GraknError.py b/grakn/common/exception.py
similarity index 94%
rename from grakn/exception/GraknError.py
rename to grakn/common/exception.py
index 022801e0..c075317e 100644
--- a/grakn/exception/GraknError.py
+++ b/grakn/common/exception.py
@@ -6,9 +6,9 @@
# to you 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
@@ -17,5 +17,5 @@
# under the License.
#
-class GraknError(Exception):
+class GraknClientException(Exception):
pass
diff --git a/grakn/concept/answer/answer.py b/grakn/concept/answer/answer.py
new file mode 100644
index 00000000..e19b2b0c
--- /dev/null
+++ b/grakn/concept/answer/answer.py
@@ -0,0 +1,35 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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 graknprotocol.protobuf.answer_pb2 as answer_proto
+
+from grakn.common.exception import GraknClientException
+from grakn.concept.answer import concept_map
+
+
+class Answer(object):
+
+ _CONCEPT_MAP = "concept_map"
+
+
+def _of(proto_answer: answer_proto.Answer):
+ answer_case = proto_answer.WhichOneof("answer")
+ if answer_case == Answer._CONCEPT_MAP:
+ return concept_map._of(proto_answer.concept_map)
+ raise GraknClientException("The answer type " + answer_case + " was not recognised.")
diff --git a/grakn/concept/answer/concept_map.py b/grakn/concept/answer/concept_map.py
new file mode 100644
index 00000000..a8f66e1e
--- /dev/null
+++ b/grakn/concept/answer/concept_map.py
@@ -0,0 +1,77 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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 typing import Mapping
+
+import graknprotocol.protobuf.answer_pb2 as answer_proto
+
+from grakn.common.exception import GraknClientException
+from grakn.concept.proto import concept_proto_reader
+from grakn.concept.answer.answer import Answer
+from grakn.concept.concept import Concept
+
+
+class ConceptMap(Answer):
+
+ _THING = "thing"
+
+ def __init__(self, mapping: Mapping[str, Concept], query_pattern: str):
+ self._map = mapping
+ self._query_pattern = query_pattern
+
+ def query_pattern(self):
+ return self._query_pattern
+
+ def map(self):
+ return self._map
+
+ def concepts(self):
+ return self._map.values()
+
+ def get(self, variable: str):
+ concept = self._map[variable]
+ if not concept:
+ raise GraknClientException("The variable " + variable + " does not exist.")
+ return concept
+
+ def __str__(self):
+ return "".join(map(lambda var: "[" + var + "/" + str(self._map[var]) + "]", sorted(self._map.keys())))
+
+ def __eq__(self, other):
+ if other is self:
+ return True
+ if not other or type(other) != type(self):
+ return False
+ return other._map == self._map
+
+ def __hash__(self):
+ return hash(self._map)
+
+
+def _of(concept_map_proto: answer_proto.ConceptMap):
+ variable_map = {}
+ for res_var in concept_map_proto.map:
+ res_concept = concept_map_proto.map[res_var]
+ if res_concept.HasField(ConceptMap._THING):
+ concept = concept_proto_reader.thing(res_concept.thing)
+ else:
+ concept = concept_proto_reader.type_(res_concept.type)
+ variable_map[res_var] = concept
+ query_pattern = None if concept_map_proto.pattern == "" else concept_map_proto.pattern
+ return ConceptMap(variable_map, query_pattern)
diff --git a/grakn/concept/concept.py b/grakn/concept/concept.py
new file mode 100644
index 00000000..47436652
--- /dev/null
+++ b/grakn/concept/concept.py
@@ -0,0 +1,48 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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.
+#
+
+
+class Concept(object):
+
+ def is_type(self):
+ return False
+
+ def is_thing(self):
+ return False
+
+ def is_remote(self):
+ return False
+
+
+class RemoteConcept(object):
+
+ def is_remote(self):
+ return True
+
+ def delete(self):
+ pass
+
+ def is_deleted(self):
+ return False
+
+ def is_type(self):
+ return False
+
+ def is_thing(self):
+ return False
diff --git a/grakn/concept/concept_manager.py b/grakn/concept/concept_manager.py
new file mode 100644
index 00000000..36bdc60b
--- /dev/null
+++ b/grakn/concept/concept_manager.py
@@ -0,0 +1,103 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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 graknprotocol.protobuf.concept_pb2 as concept_proto
+import graknprotocol.protobuf.transaction_pb2 as transaction_proto
+
+from grakn.concept.proto import concept_proto_reader, concept_proto_builder
+from grakn.concept.type.entity_type import EntityType
+from grakn.concept.type.relation_type import RelationType
+
+
+class ConceptManager(object):
+
+ def __init__(self, transaction):
+ self._transaction = transaction
+
+ def get_root_thing_type(self):
+ return self.get_type("thing")
+
+ def get_root_entity_type(self):
+ return self.get_entity_type("entity")
+
+ def get_root_relation_type(self):
+ return self.get_relation_type("relation")
+
+ def get_root_attribute_type(self):
+ return self.get_attribute_type("attribute")
+
+ def put_entity_type(self, label: str):
+ req = concept_proto.ConceptManager.Req()
+ put_entity_type_req = concept_proto.ConceptManager.PutEntityType.Req()
+ put_entity_type_req.label = label
+ req.put_entity_type_req.CopyFrom(put_entity_type_req)
+ res = self._execute(req)
+ return EntityType._of(res.put_entity_type_res.entity_type)
+
+ def get_entity_type(self, label: str):
+ _type = self.get_type(label)
+ return _type if _type.is_entity_type() else None
+
+ def put_relation_type(self, label: str):
+ req = concept_proto.ConceptManager.Req()
+ put_relation_type_req = concept_proto.ConceptManager.PutRelationType.Req()
+ put_relation_type_req.label = label
+ req.put_relation_type_req.CopyFrom(put_relation_type_req)
+ res = self._execute(req)
+ return RelationType._of(res.put_relation_type_res.relation_type)
+
+ def get_relation_type(self, label: str):
+ _type = self.get_type(label)
+ return _type if _type.is_relation_type() else None
+
+ def put_attribute_type(self, label: str, value_type):
+ req = concept_proto.ConceptManager.Req()
+ put_attribute_type_req = concept_proto.ConceptManager.PutAttributeType.Req()
+ put_attribute_type_req.label = label
+ put_attribute_type_req.value_type = concept_proto_builder.value_type(value_type)
+ req.put_attribute_type_req.CopyFrom(put_attribute_type_req)
+ res = self._execute(req)
+ return concept_proto_reader.attribute_type(res.put_attribute_type_res.attribute_type)
+
+ def get_attribute_type(self, label: str):
+ _type = self.get_type(label)
+ return _type if _type.is_attribute_type() else None
+
+ def get_thing(self, iid: str):
+ req = concept_proto.ConceptManager.Req()
+ get_thing_req = concept_proto.ConceptManager.GetThing.Req()
+ get_thing_req.iid = concept_proto_builder.iid(iid)
+ req.get_thing_req.CopyFrom(get_thing_req)
+
+ response = self._execute(req)
+ return concept_proto_reader.thing(response.get_thing_res.thing) if response.get_thing_res.WhichOneof("res") == "thing" else None
+
+ def get_type(self, label: str):
+ req = concept_proto.ConceptManager.Req()
+ get_type_req = concept_proto.ConceptManager.GetType.Req()
+ get_type_req.label = label
+ req.get_type_req.CopyFrom(get_type_req)
+
+ response = self._execute(req)
+ return concept_proto_reader.type_(response.get_type_res.type) if response.get_type_res.WhichOneof("res") == "type" else None
+
+ def _execute(self, request: concept_proto.ConceptManager.Req):
+ req = transaction_proto.Transaction.Req()
+ req.concept_manager_req.CopyFrom(request)
+ return self._transaction._execute(req).concept_manager_res
diff --git a/grakn/concept/proto/concept_proto_builder.py b/grakn/concept/proto/concept_proto_builder.py
new file mode 100644
index 00000000..ba4c473a
--- /dev/null
+++ b/grakn/concept/proto/concept_proto_builder.py
@@ -0,0 +1,125 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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 datetime import datetime
+from typing import List
+
+import graknprotocol.protobuf.concept_pb2 as concept_proto
+
+from grakn.common.exception import GraknClientException
+from grakn.concept.type.value_type import ValueType
+
+
+def thing(thing_):
+ proto_thing = concept_proto.Thing()
+ proto_thing.iid = bytes.fromhex(thing_.get_iid())
+ proto_thing.encoding = thing_encoding(thing_)
+ return proto_thing
+
+
+def type_(_type):
+ proto_type = concept_proto.Type()
+ proto_type.label = _type.get_label()
+ proto_type.encoding = type_encoding(_type)
+
+ if _type.is_role_type():
+ proto_type.scope = _type.get_scope()
+
+ return proto_type
+
+
+def types(types_: List):
+ return map(lambda _type: type_(_type), types_)
+
+
+def boolean_attribute_value(value: bool):
+ value_proto = concept_proto.Attribute.Value()
+ value_proto.boolean = value
+ return value_proto
+
+
+def long_attribute_value(value: int):
+ value_proto = concept_proto.Attribute.Value()
+ value_proto.long = value
+ return value_proto
+
+
+def double_attribute_value(value: float):
+ value_proto = concept_proto.Attribute.Value()
+ value_proto.double = value
+ return value_proto
+
+
+def string_attribute_value(value: str):
+ value_proto = concept_proto.Attribute.Value()
+ value_proto.string = value
+ return value_proto
+
+
+def datetime_attribute_value(value: datetime):
+ value_proto = concept_proto.Attribute.Value()
+ value_proto.date_time = int((value - datetime(1970, 1, 1)).total_seconds() * 1000)
+ return value_proto
+
+
+def value_type(value_type_: ValueType):
+ if value_type_ == ValueType.BOOLEAN:
+ return concept_proto.AttributeType.ValueType.Value("BOOLEAN")
+ elif value_type_ == ValueType.LONG:
+ return concept_proto.AttributeType.ValueType.Value("LONG")
+ elif value_type_ == ValueType.DOUBLE:
+ return concept_proto.AttributeType.ValueType.Value("DOUBLE")
+ elif value_type_ == ValueType.STRING:
+ return concept_proto.AttributeType.ValueType.Value("STRING")
+ elif value_type_ == ValueType.DATETIME:
+ return concept_proto.AttributeType.ValueType.Value("DATETIME")
+ elif value_type_ == ValueType.OBJECT:
+ return concept_proto.AttributeType.ValueType.Value("OBJECT")
+ else:
+ raise GraknClientException("Unrecognised value type: " + str(value_type_))
+
+
+def iid(iid_: str):
+ return bytes.fromhex(iid_)
+
+
+def thing_encoding(thing_):
+ if thing_.is_entity():
+ return concept_proto.Thing.Encoding.Value("ENTITY")
+ elif thing_.is_relation():
+ return concept_proto.Thing.Encoding.Value("RELATION")
+ elif thing_.is_attribute():
+ return concept_proto.Thing.Encoding.Value("ATTRIBUTE")
+ else:
+ raise GraknClientException("Unrecognised thing encoding: " + str(thing_))
+
+
+def type_encoding(_type):
+ if _type.is_entity_type():
+ return concept_proto.Type.Encoding.Value("ENTITY_TYPE")
+ elif _type.is_relation_type():
+ return concept_proto.Type.Encoding.Value("RELATION_TYPE")
+ elif _type.is_attribute_type():
+ return concept_proto.Type.Encoding.Value("ATTRIBUTE_TYPE")
+ elif _type.is_role_type():
+ return concept_proto.Type.Encoding.Value("ROLE_TYPE")
+ elif _type.is_thing_type():
+ return concept_proto.Type.Encoding.Value("THING_TYPE")
+ else:
+ raise GraknClientException("Unrecognised type encoding: " + str(_type))
diff --git a/grakn/concept/proto/concept_proto_reader.py b/grakn/concept/proto/concept_proto_reader.py
new file mode 100644
index 00000000..3fc2b27d
--- /dev/null
+++ b/grakn/concept/proto/concept_proto_reader.py
@@ -0,0 +1,95 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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 graknprotocol.protobuf.concept_pb2 as concept_proto
+
+from grakn.common.exception import GraknClientException
+from grakn.concept.thing.attribute import BooleanAttribute, LongAttribute, DoubleAttribute, StringAttribute, \
+ DateTimeAttribute
+from grakn.concept.thing.entity import Entity
+from grakn.concept.thing.relation import Relation
+from grakn.concept.type.attribute_type import BooleanAttributeType, LongAttributeType, DoubleAttributeType, \
+ StringAttributeType, DateTimeAttributeType, AttributeType
+from grakn.concept.type.entity_type import EntityType
+from grakn.concept.type.relation_type import RelationType
+from grakn.concept.type.role_type import RoleType
+from grakn.concept.type.thing_type import ThingType
+
+
+def thing(thing_proto: concept_proto.Thing):
+ if thing_proto.encoding == concept_proto.Thing.Encoding.Value("ENTITY"):
+ return Entity._of(thing_proto)
+ elif thing_proto.encoding == concept_proto.Thing.Encoding.Value("RELATION"):
+ return Relation._of(thing_proto)
+ elif thing_proto.encoding == concept_proto.Thing.Encoding.Value("ATTRIBUTE"):
+ return attribute(thing_proto)
+ else:
+ raise GraknClientException("The encoding " + thing_proto.encoding + " was not recognised.")
+
+
+def attribute(thing_proto: concept_proto.Thing):
+ if thing_proto.value_type == concept_proto.AttributeType.ValueType.Value("BOOLEAN"):
+ return BooleanAttribute._of(thing_proto)
+ elif thing_proto.value_type == concept_proto.AttributeType.ValueType.Value("LONG"):
+ return LongAttribute._of(thing_proto)
+ elif thing_proto.value_type == concept_proto.AttributeType.ValueType.Value("DOUBLE"):
+ return DoubleAttribute._of(thing_proto)
+ elif thing_proto.value_type == concept_proto.AttributeType.ValueType.Value("STRING"):
+ return StringAttribute._of(thing_proto)
+ elif thing_proto.value_type == concept_proto.AttributeType.ValueType.Value("DATETIME"):
+ return DateTimeAttribute._of(thing_proto)
+ else:
+ raise GraknClientException("The value type " + str(thing_proto.value_type) + " was not recognised.")
+
+
+def type_(type_proto: concept_proto.Type):
+ if type_proto.encoding == concept_proto.Type.Encoding.Value("ROLE_TYPE"):
+ return RoleType._of(type_proto)
+ else:
+ return thing_type(type_proto)
+
+
+def thing_type(type_proto: concept_proto.Type):
+ if type_proto.encoding == concept_proto.Type.Encoding.Value("ENTITY_TYPE"):
+ return EntityType._of(type_proto)
+ elif type_proto.encoding == concept_proto.Type.Encoding.Value("RELATION_TYPE"):
+ return RelationType._of(type_proto)
+ elif type_proto.encoding == concept_proto.Type.Encoding.Value("ATTRIBUTE_TYPE"):
+ return attribute_type(type_proto)
+ elif type_proto.encoding == concept_proto.Type.Encoding.Value("THING_TYPE"):
+ return ThingType(type_proto.label, type_proto.root)
+ else:
+ raise GraknClientException("The encoding " + str(type_proto.encoding) + " was not recognised.")
+
+
+def attribute_type(type_proto: concept_proto.Type):
+ if type_proto.value_type == concept_proto.AttributeType.ValueType.Value("BOOLEAN"):
+ return BooleanAttributeType._of(type_proto)
+ elif type_proto.value_type == concept_proto.AttributeType.ValueType.Value("LONG"):
+ return LongAttributeType._of(type_proto)
+ elif type_proto.value_type == concept_proto.AttributeType.ValueType.Value("DOUBLE"):
+ return DoubleAttributeType._of(type_proto)
+ elif type_proto.value_type == concept_proto.AttributeType.ValueType.Value("STRING"):
+ return StringAttributeType._of(type_proto)
+ elif type_proto.value_type == concept_proto.AttributeType.ValueType.Value("DATETIME"):
+ return DateTimeAttributeType._of(type_proto)
+ elif type_proto.value_type == concept_proto.AttributeType.ValueType.Value("OBJECT"):
+ return AttributeType(type_proto.label, type_proto.root)
+ else:
+ raise GraknClientException("The value type " + str(type_proto.value_type) + " was not recognised.")
diff --git a/grakn/concept/thing/attribute.py b/grakn/concept/thing/attribute.py
new file mode 100644
index 00000000..7e08ecc7
--- /dev/null
+++ b/grakn/concept/thing/attribute.py
@@ -0,0 +1,255 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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 datetime import datetime
+
+import graknprotocol.protobuf.concept_pb2 as concept_proto
+
+from grakn.concept.proto import concept_proto_builder
+from grakn.concept.thing.thing import Thing, RemoteThing
+
+
+class Attribute(Thing):
+
+ def is_attribute(self):
+ return True
+
+ def is_boolean(self):
+ return False
+
+ def is_long(self):
+ return False
+
+ def is_double(self):
+ return False
+
+ def is_string(self):
+ return False
+
+ def is_datetime(self):
+ return False
+
+
+class RemoteAttribute(RemoteThing):
+
+ def get_owners(self, owner_type=None):
+ method = concept_proto.Thing.Req()
+ get_owners_req = concept_proto.Attribute.GetOwners.Req()
+ if owner_type:
+ get_owners_req.thing_type = concept_proto_builder.type_(owner_type)
+ method.attribute_get_owners_req.CopyFrom(get_owners_req)
+ return self._thing_stream(method, lambda res: res.attribute_get_owners_res.things)
+
+ def is_attribute(self):
+ return True
+
+ def is_boolean(self):
+ return False
+
+ def is_long(self):
+ return False
+
+ def is_double(self):
+ return False
+
+ def is_string(self):
+ return False
+
+ def is_datetime(self):
+ return False
+
+
+class BooleanAttribute(Attribute):
+
+ def __init__(self, iid: str, value: bool):
+ super(BooleanAttribute, self).__init__(iid)
+ self._value = value
+
+ @staticmethod
+ def _of(thing_proto: concept_proto.Thing):
+ return BooleanAttribute(thing_proto.iid.hex(), thing_proto.value.boolean)
+
+ def get_value(self):
+ return self._value
+
+ def is_boolean(self):
+ return True
+
+ def as_remote(self, transaction):
+ return RemoteBooleanAttribute(transaction, self.get_iid(), self.get_value())
+
+
+class RemoteBooleanAttribute(RemoteAttribute):
+
+ def __init__(self, transaction, iid: str, value: bool):
+ super(RemoteBooleanAttribute, self).__init__(transaction, iid)
+ self._value = value
+
+ def get_value(self):
+ return self._value
+
+ def is_boolean(self):
+ return True
+
+ def as_remote(self, transaction):
+ return RemoteBooleanAttribute(transaction, self.get_iid(), self.get_value())
+
+
+class LongAttribute(Attribute):
+
+ def __init__(self, iid: str, value: int):
+ super(LongAttribute, self).__init__(iid)
+ self._value = value
+
+ @staticmethod
+ def _of(thing_proto: concept_proto.Thing):
+ return LongAttribute(thing_proto.iid.hex(), thing_proto.value.long)
+
+ def get_value(self):
+ return self._value
+
+ def is_long(self):
+ return True
+
+ def as_remote(self, transaction):
+ return RemoteLongAttribute(transaction, self.get_iid(), self.get_value())
+
+
+class RemoteLongAttribute(RemoteAttribute):
+
+ def __init__(self, transaction, iid: str, value: int):
+ super(RemoteLongAttribute, self).__init__(transaction, iid)
+ self._value = value
+
+ def get_value(self):
+ return self._value
+
+ def is_long(self):
+ return True
+
+ def as_remote(self, transaction):
+ return RemoteLongAttribute(transaction, self.get_iid(), self.get_value())
+
+
+class DoubleAttribute(Attribute):
+
+ def __init__(self, iid: str, value: float):
+ super(DoubleAttribute, self).__init__(iid)
+ self._value = value
+
+ @staticmethod
+ def _of(thing_proto: concept_proto.Thing):
+ return DoubleAttribute(thing_proto.iid.hex(), thing_proto.value.double)
+
+ def get_value(self):
+ return self._value
+
+ def is_double(self):
+ return True
+
+ def as_remote(self, transaction):
+ return RemoteDoubleAttribute(transaction, self.get_iid(), self.get_value())
+
+
+class RemoteDoubleAttribute(RemoteAttribute):
+
+ def __init__(self, transaction, iid: str, value: float):
+ super(RemoteDoubleAttribute, self).__init__(transaction, iid)
+ self._value = value
+
+ def get_value(self):
+ return self._value
+
+ def is_double(self):
+ return True
+
+ def as_remote(self, transaction):
+ return RemoteDoubleAttribute(transaction, self.get_iid(), self.get_value())
+
+
+class StringAttribute(Attribute):
+
+ def __init__(self, iid: str, value: str):
+ super(StringAttribute, self).__init__(iid)
+ self._value = value
+
+ @staticmethod
+ def _of(thing_proto: concept_proto.Thing):
+ return StringAttribute(thing_proto.iid.hex(), thing_proto.value.string)
+
+ def get_value(self):
+ return self._value
+
+ def is_string(self):
+ return True
+
+ def as_remote(self, transaction):
+ return RemoteStringAttribute(transaction, self.get_iid(), self.get_value())
+
+
+class RemoteStringAttribute(RemoteAttribute):
+
+ def __init__(self, transaction, iid: str, value: str):
+ super(RemoteStringAttribute, self).__init__(transaction, iid)
+ self._value = value
+
+ def get_value(self):
+ return self._value
+
+ def is_string(self):
+ return True
+
+ def as_remote(self, transaction):
+ return RemoteStringAttribute(transaction, self.get_iid(), self.get_value())
+
+
+class DateTimeAttribute(Attribute):
+
+ def __init__(self, iid: str, value: datetime):
+ super(DateTimeAttribute, self).__init__(iid)
+ self._value = value
+
+ @staticmethod
+ def _of(thing_proto: concept_proto.Thing):
+ return DateTimeAttribute(thing_proto.iid.hex(), datetime.fromtimestamp(float(thing_proto.value.date_time) / 1000.0))
+
+ def get_value(self):
+ return self._value
+
+ def is_datetime(self):
+ return True
+
+ def as_remote(self, transaction):
+ return RemoteDateTimeAttribute(transaction, self.get_iid(), self.get_value())
+
+
+class RemoteDateTimeAttribute(RemoteAttribute):
+
+ def __init__(self, transaction, iid: str, value: datetime):
+ super(RemoteDateTimeAttribute, self).__init__(transaction, iid)
+ self._value = value
+
+ def get_value(self):
+ return self._value
+
+ def is_datetime(self):
+ return True
+
+ def as_remote(self, transaction):
+ return RemoteDateTimeAttribute(transaction, self.get_iid(), self.get_value())
diff --git a/grakn/concept/thing/entity.py b/grakn/concept/thing/entity.py
new file mode 100644
index 00000000..971ffb74
--- /dev/null
+++ b/grakn/concept/thing/entity.py
@@ -0,0 +1,44 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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 graknprotocol.protobuf.concept_pb2 as concept_proto
+
+from grakn.concept.thing.thing import Thing, RemoteThing
+
+
+class Entity(Thing):
+
+ @staticmethod
+ def _of(thing_proto: concept_proto.Thing):
+ return Entity(thing_proto.iid.hex())
+
+ def as_remote(self, transaction):
+ return RemoteEntity(transaction, self._iid)
+
+ def is_entity(self):
+ return True
+
+
+class RemoteEntity(RemoteThing):
+
+ def as_remote(self, transaction):
+ return RemoteEntity(transaction, self._iid)
+
+ def is_entity(self):
+ return True
diff --git a/grakn/concept/thing/relation.py b/grakn/concept/thing/relation.py
new file mode 100644
index 00000000..6a28b9ca
--- /dev/null
+++ b/grakn/concept/thing/relation.py
@@ -0,0 +1,89 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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 graknprotocol.protobuf.concept_pb2 as concept_proto
+import graknprotocol.protobuf.transaction_pb2 as transaction_proto
+
+from grakn.concept.proto import concept_proto_builder, concept_proto_reader
+from grakn.concept.thing.thing import Thing, RemoteThing
+
+
+class Relation(Thing):
+
+ @staticmethod
+ def _of(thing_proto: concept_proto.Thing):
+ return Relation(thing_proto.iid.hex())
+
+ def as_remote(self, transaction):
+ return RemoteRelation(transaction, self.get_iid())
+
+ def is_relation(self):
+ return True
+
+
+class RemoteRelation(RemoteThing):
+
+ def as_remote(self, transaction):
+ return RemoteRelation(transaction, self.get_iid())
+
+ def get_players_by_role_type(self):
+ method = concept_proto.Thing.Req()
+ method.relation_get_players_by_role_type_req.CopyFrom(concept_proto.Relation.GetPlayersByRoleType.Req())
+ method.iid = concept_proto_builder.iid(self.get_iid())
+
+ request = transaction_proto.Transaction.Req()
+ request.thing_req.CopyFrom(method)
+ stream = self._transaction._stream(request, lambda res: res.thing_res.relation_get_players_by_role_type_res.role_types_with_players)
+
+ role_player_dict = {}
+ for role_player in stream:
+ role = concept_proto_reader.type_(role_player.role_type)
+ player = concept_proto_reader.thing(role_player.player)
+ if role not in role_player_dict:
+ role_player_dict[role] = []
+ role_player_dict[role].append(player)
+ return role_player_dict
+
+ def get_players(self, role_types=None):
+ if not role_types:
+ role_types = []
+ method = concept_proto.Thing.Req()
+ get_players_req = concept_proto.Relation.GetPlayers.Req()
+ get_players_req.role_types.extend(concept_proto_builder.types(role_types))
+ method.relation_get_players_req.CopyFrom(get_players_req)
+ return self._thing_stream(method, lambda res: res.relation_get_players_res.things)
+
+ def add_player(self, role_type, player):
+ method = concept_proto.Thing.Req()
+ add_player_req = concept_proto.Relation.AddPlayer.Req()
+ add_player_req.role_type.CopyFrom(concept_proto_builder.type_(role_type))
+ add_player_req.player.CopyFrom(concept_proto_builder.thing(player))
+ method.relation_add_player_req.CopyFrom(add_player_req)
+ self._execute(method)
+
+ def remove_player(self, role_type, player):
+ method = concept_proto.Thing.Req()
+ remove_player_req = concept_proto.Relation.RemovePlayer.Req()
+ remove_player_req.role_type.CopyFrom(concept_proto_builder.type_(role_type))
+ remove_player_req.player.CopyFrom(concept_proto_builder.thing(player))
+ method.relation_remove_player_req.CopyFrom(remove_player_req)
+ self._execute(method)
+
+ def is_relation(self):
+ return True
diff --git a/grakn/concept/thing/thing.py b/grakn/concept/thing/thing.py
new file mode 100644
index 00000000..c5683631
--- /dev/null
+++ b/grakn/concept/thing/thing.py
@@ -0,0 +1,183 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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 typing import Callable, List
+
+import graknprotocol.protobuf.concept_pb2 as concept_proto
+import graknprotocol.protobuf.transaction_pb2 as transaction_proto
+
+from grakn.common.exception import GraknClientException
+from grakn.concept.proto import concept_proto_reader, concept_proto_builder
+from grakn.concept.concept import Concept, RemoteConcept
+
+
+class Thing(Concept):
+
+ def __init__(self, iid: str):
+ if not iid:
+ raise GraknClientException("IID must be a non-empty string.")
+ self._iid = iid
+ self._hash = hash(iid)
+
+ def get_iid(self):
+ return self._iid
+
+ def is_thing(self):
+ return True
+
+ def is_entity(self):
+ return False
+
+ def is_attribute(self):
+ return False
+
+ def is_relation(self):
+ return False
+
+ def __str__(self):
+ return type(self).__name__ + "[iid:" + self.get_iid() + "]"
+
+ def __eq__(self, other):
+ if other is self:
+ return True
+ if not other or type(self) != type(other):
+ return False
+ return self.get_iid() == other.get_iid()
+
+ def __hash__(self):
+ return self._hash
+
+
+class RemoteThing(RemoteConcept):
+
+ def __init__(self, transaction, iid: str):
+ if not transaction:
+ raise GraknClientException("Transaction must be set.")
+ if not iid:
+ raise GraknClientException("IID must be set.")
+ self._transaction = transaction
+ self._iid = iid
+ self._hash = hash(iid)
+
+ def get_iid(self):
+ return self._iid
+
+ def get_type(self):
+ method = concept_proto.Thing.Req()
+ method.thing_get_type_req.CopyFrom(concept_proto.Thing.GetType.Req())
+ return concept_proto_reader.type_(self._execute(method).thing_get_type_res.thing_type)
+
+ def is_inferred(self):
+ req = concept_proto.Thing.Req()
+ req.thing_is_inferred_req.CopyFrom(concept_proto.Thing.IsInferred.Req())
+ return self._execute(req).thing_is_inferred_res.inferred
+
+ def get_has(self, attribute_type=None, attribute_types: List = None, only_key=False):
+ if [bool(attribute_type), bool(attribute_types), only_key].count(True) > 1:
+ raise GraknClientException("Only one filter can be applied at a time to get_has."
+ "The possible filters are: [attribute_type, attribute_types, only_key]")
+ if attribute_type:
+ attribute_types = [attribute_type]
+ method = concept_proto.Thing.Req()
+ get_has_req = concept_proto.Thing.GetHas.Req()
+ if only_key:
+ get_has_req.keys_only = only_key
+ elif attribute_types:
+ get_has_req.attribute_types.extend(concept_proto_builder.types(attribute_types))
+ method.thing_get_has_req.CopyFrom(get_has_req)
+ return self._thing_stream(method, lambda res: res.thing_get_has_res.attributes)
+
+ def get_plays(self):
+ req = concept_proto.Thing.Req()
+ req.thing_get_plays_req.CopyFrom(concept_proto.Thing.GetPlays.Req())
+ return self._type_stream(req, lambda res: res.thing_get_plays_res.role_types)
+
+ def get_relations(self, role_types=None):
+ if not role_types:
+ role_types = []
+ method = concept_proto.Thing.Req()
+ get_relations_req = concept_proto.Thing.GetRelations.Req()
+ get_relations_req.role_types.extend(concept_proto_builder.types(role_types))
+ method.thing_get_relations_req.CopyFrom(get_relations_req)
+ return self._thing_stream(method, lambda res: res.thing_get_relations_res.relations)
+
+ def set_has(self, attribute):
+ method = concept_proto.Thing.Req()
+ set_has_req = concept_proto.Thing.SetHas.Req()
+ set_has_req.attribute.CopyFrom(concept_proto_builder.thing(attribute))
+ method.thing_set_has_req.CopyFrom(set_has_req)
+ self._execute(method)
+
+ def unset_has(self, attribute):
+ method = concept_proto.Thing.Req()
+ unset_has_req = concept_proto.Thing.UnsetHas.Req()
+ unset_has_req.attribute.CopyFrom(concept_proto_builder.thing(attribute))
+ method.thing_unset_has_req.CopyFrom(unset_has_req)
+ self._execute(method)
+
+ def delete(self):
+ method = concept_proto.Thing.Req()
+ method.thing_delete_req.CopyFrom(method.concept_proto.Thing.Delete.Req())
+ self._execute(method)
+
+ def is_deleted(self):
+ return not self._transaction.concepts().get_thing(self.get_iid())
+
+ def is_thing(self):
+ return True
+
+ def is_entity(self):
+ return False
+
+ def is_attribute(self):
+ return False
+
+ def is_relation(self):
+ return False
+
+ def _thing_stream(self, method: concept_proto.Thing.Req, thing_list_getter: Callable[[concept_proto.Thing.Res], List[concept_proto.Thing]]):
+ method.iid = concept_proto_builder.iid(self.get_iid())
+ request = transaction_proto.Transaction.Req()
+ request.thing_req.CopyFrom(method)
+ return map(lambda thing_proto: concept_proto_reader.thing(thing_proto), self._transaction._stream(request, lambda res: thing_list_getter(res.thing_res)))
+
+ def _type_stream(self, method: concept_proto.Thing.Req, type_list_getter: Callable[[concept_proto.Thing.Res], List[concept_proto.Type]]):
+ method.iid = concept_proto_builder.iid(self.get_iid())
+ request = transaction_proto.Transaction.Req()
+ request.thing_req.CopyFrom(method)
+ return map(lambda type_proto: concept_proto_reader.type_(type_proto), self._transaction._stream(request, lambda res: type_list_getter(res.thing_res)))
+
+ def _execute(self, method: concept_proto.Thing.Req):
+ method.iid = concept_proto_builder.iid(self.get_iid())
+ request = transaction_proto.Transaction.Req()
+ request.thing_req.CopyFrom(method)
+ return self._transaction._execute(request).thing_res
+
+ def __str__(self):
+ return type(self).__name__ + "[iid:" + str(self._iid) + "]"
+
+ def __eq__(self, other):
+ if other is self:
+ return True
+ if not other or type(self) != type(other):
+ return False
+ return self._transaction is other._transaction and self._iid == other._iid
+
+ def __hash__(self):
+ return self._hash
diff --git a/grakn/concept/type/attribute_type.py b/grakn/concept/type/attribute_type.py
new file mode 100644
index 00000000..6f17af4e
--- /dev/null
+++ b/grakn/concept/type/attribute_type.py
@@ -0,0 +1,303 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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 datetime import datetime
+
+import graknprotocol.protobuf.concept_pb2 as concept_proto
+
+from grakn.concept.proto import concept_proto_builder, concept_proto_reader
+from grakn.concept.type.thing_type import ThingType, RemoteThingType
+from grakn.concept.type.value_type import ValueType
+
+
+def is_keyable(value_type: ValueType):
+ return value_type in [ValueType.LONG, ValueType.STRING, ValueType.DATETIME]
+
+
+class AttributeType(ThingType):
+
+ def as_remote(self, transaction):
+ return RemoteAttributeType(transaction, self.get_label(), self.is_root())
+
+ def get_value_type(self):
+ return ValueType.OBJECT
+
+ def is_keyable(self):
+ return is_keyable(self.get_value_type())
+
+ def is_attribute_type(self):
+ return True
+
+ def is_boolean(self):
+ return False
+
+ def is_long(self):
+ return False
+
+ def is_double(self):
+ return False
+
+ def is_string(self):
+ return False
+
+ def is_datetime(self):
+ return False
+
+ def __eq__(self, other):
+ if other is self:
+ return True
+ # root "attribute" should always be equal to itself regardless of which value class it holds
+ if not other or not isinstance(AttributeType, other):
+ return False
+ return self.get_label() == other.get_label()
+
+ def __hash__(self):
+ return super(AttributeType, self).__hash__()
+
+
+class RemoteAttributeType(RemoteThingType):
+
+ def get_value_type(self):
+ return ValueType.OBJECT
+
+ def is_keyable(self):
+ return is_keyable(self.get_value_type())
+
+ def as_remote(self, transaction):
+ return RemoteAttributeType(transaction, self.get_label(), self.is_root())
+
+ def get_owners(self, only_key=False):
+ method = concept_proto.Type.Req()
+ get_owners_req = concept_proto.AttributeType.GetOwners.Req()
+ get_owners_req.only_key = only_key
+ method.attribute_type_get_owners_req.CopyFrom(get_owners_req)
+ return self._type_stream(method, lambda res: res.attribute_type_get_owners_res.owners)
+
+ def _put_internal(self, value_proto):
+ method = concept_proto.Type.Req()
+ put_req = concept_proto.AttributeType.Put.Req()
+ put_req.value.CopyFrom(value_proto)
+ method.attribute_type_put_req.CopyFrom(put_req)
+ return concept_proto_reader.attribute(self._execute(method).attribute_type_put_res.attribute)
+
+ def _get_internal(self, value_proto):
+ method = concept_proto.Type.Req()
+ get_req = concept_proto.AttributeType.Get.Req()
+ get_req.value.CopyFrom(value_proto)
+ method.attribute_type_get_req.CopyFrom(get_req)
+ response = self._execute(method).attribute_type_get_res
+ return concept_proto_reader.attribute(response.attribute) if response.WhichOneof("res") == "attribute" else None
+
+ def is_attribute_type(self):
+ return True
+
+ def is_boolean(self):
+ return False
+
+ def is_long(self):
+ return False
+
+ def is_double(self):
+ return False
+
+ def is_string(self):
+ return False
+
+ def is_datetime(self):
+ return False
+
+ def __eq__(self, other):
+ if other is self:
+ return True
+ if not other or not isinstance(RemoteAttributeType, other):
+ return False
+ return self.get_label() == other.get_label()
+
+ def __hash__(self):
+ return super(RemoteAttributeType, self).__hash__()
+
+
+class BooleanAttributeType(AttributeType):
+
+ @staticmethod
+ def _of(type_proto: concept_proto.Type):
+ return BooleanAttributeType(type_proto.label, type_proto.root)
+
+ def get_value_type(self):
+ return ValueType.BOOLEAN
+
+ def as_remote(self, transaction):
+ return RemoteBooleanAttributeType(transaction, self.get_label(), self.is_root())
+
+ def is_boolean(self):
+ return True
+
+
+class RemoteBooleanAttributeType(RemoteAttributeType):
+
+ def get_value_type(self):
+ return ValueType.BOOLEAN
+
+ def as_remote(self, transaction):
+ return RemoteBooleanAttributeType(transaction, self.get_label(), self.is_root())
+
+ def put(self, value: bool):
+ return self._put_internal(concept_proto_builder.boolean_attribute_value(value))
+
+ def get(self, value: bool):
+ return self._get_internal(concept_proto_builder.boolean_attribute_value(value))
+
+ def is_boolean(self):
+ return True
+
+
+class LongAttributeType(AttributeType):
+
+ @staticmethod
+ def _of(type_proto: concept_proto.Type):
+ return LongAttributeType(type_proto.label, type_proto.root)
+
+ def get_value_type(self):
+ return ValueType.LONG
+
+ def as_remote(self, transaction):
+ return RemoteLongAttributeType(transaction, self.get_label(), self.is_root())
+
+ def is_long(self):
+ return True
+
+
+class RemoteLongAttributeType(RemoteAttributeType):
+
+ def get_value_type(self):
+ return ValueType.LONG
+
+ def as_remote(self, transaction):
+ return RemoteLongAttributeType(transaction, self.get_label(), self.is_root())
+
+ def put(self, value: int):
+ return self._put_internal(concept_proto_builder.long_attribute_value(value))
+
+ def get(self, value: int):
+ return self._get_internal(concept_proto_builder.long_attribute_value(value))
+
+ def is_long(self):
+ return True
+
+
+class DoubleAttributeType(AttributeType):
+
+ @staticmethod
+ def _of(type_proto: concept_proto.Type):
+ return DoubleAttributeType(type_proto.label, type_proto.root)
+
+ def get_value_type(self):
+ return ValueType.DOUBLE
+
+ def as_remote(self, transaction):
+ return RemoteDoubleAttributeType(transaction, self.get_label(), self.is_root())
+
+ def is_double(self):
+ return True
+
+
+class RemoteDoubleAttributeType(RemoteAttributeType):
+
+ def get_value_type(self):
+ return ValueType.DOUBLE
+
+ def as_remote(self, transaction):
+ return RemoteDoubleAttributeType(transaction, self.get_label(), self.is_root())
+
+ def put(self, value: float):
+ return self._put_internal(concept_proto_builder.double_attribute_value(value))
+
+ def get(self, value: float):
+ return self._get_internal(concept_proto_builder.double_attribute_value(value))
+
+ def is_double(self):
+ return True
+
+
+class StringAttributeType(AttributeType):
+
+ @staticmethod
+ def _of(type_proto: concept_proto.Type):
+ return StringAttributeType(type_proto.label, type_proto.root)
+
+ def get_value_type(self):
+ return ValueType.STRING
+
+ def as_remote(self, transaction):
+ return RemoteStringAttributeType(transaction, self.get_label(), self.is_root())
+
+ def is_string(self):
+ return True
+
+
+class RemoteStringAttributeType(RemoteAttributeType):
+
+ def get_value_type(self):
+ return ValueType.STRING
+
+ def as_remote(self, transaction):
+ return RemoteStringAttributeType(transaction, self.get_label(), self.is_root())
+
+ def put(self, value: str):
+ return self._put_internal(concept_proto_builder.string_attribute_value(value))
+
+ def get(self, value: str):
+ return self._get_internal(concept_proto_builder.string_attribute_value(value))
+
+ def is_string(self):
+ return True
+
+
+class DateTimeAttributeType(AttributeType):
+
+ @staticmethod
+ def _of(type_proto: concept_proto.Type):
+ return DateTimeAttributeType(type_proto.label, type_proto.root)
+
+ def get_value_type(self):
+ return ValueType.DATETIME
+
+ def as_remote(self, transaction):
+ return RemoteDateTimeAttributeType(transaction, self.get_label(), self.is_root())
+
+ def is_datetime(self):
+ return True
+
+
+class RemoteDateTimeAttributeType(RemoteAttributeType):
+
+ def get_value_type(self):
+ return ValueType.DATETIME
+
+ def as_remote(self, transaction):
+ return RemoteDateTimeAttributeType(transaction, self.get_label(), self.is_root())
+
+ def put(self, value: datetime):
+ return self._put_internal(concept_proto_builder.datetime_attribute_value(value))
+
+ def get(self, value: datetime):
+ return self._get_internal(concept_proto_builder.datetime_attribute_value(value))
+
+ def is_datetime(self):
+ return True
diff --git a/grakn/concept/type/entity_type.py b/grakn/concept/type/entity_type.py
new file mode 100644
index 00000000..77de00de
--- /dev/null
+++ b/grakn/concept/type/entity_type.py
@@ -0,0 +1,51 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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 graknprotocol.protobuf.concept_pb2 as concept_proto
+
+from grakn.concept.thing.entity import Entity
+from grakn.concept.type.thing_type import ThingType, RemoteThingType
+
+
+class EntityType(ThingType):
+
+ @staticmethod
+ def _of(type_proto: concept_proto.Type):
+ return EntityType(type_proto.label, type_proto.root)
+
+ def as_remote(self, transaction):
+ return RemoteEntityType(transaction, self.get_label(), self.is_root())
+
+ def is_entity_type(self):
+ return True
+
+
+class RemoteEntityType(RemoteThingType):
+
+ def as_remote(self, transaction):
+ return RemoteEntityType(transaction, self.get_label(), self.is_root())
+
+ def create(self):
+ method = concept_proto.Type.Req()
+ create_req = concept_proto.EntityType.Create.Req()
+ method.entity_type_create_req.CopyFrom(create_req)
+ return Entity._of(self._execute(method).entity_type_create_res.entity)
+
+ def is_entity_type(self):
+ return True
diff --git a/grakn/concept/type/relation_type.py b/grakn/concept/type/relation_type.py
new file mode 100644
index 00000000..b4115d91
--- /dev/null
+++ b/grakn/concept/type/relation_type.py
@@ -0,0 +1,77 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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 graknprotocol.protobuf.concept_pb2 as concept_proto
+
+from grakn.concept.proto import concept_proto_reader
+from grakn.concept.thing.relation import Relation
+from grakn.concept.type.thing_type import ThingType, RemoteThingType
+
+
+class RelationType(ThingType):
+
+ @staticmethod
+ def _of(type_proto: concept_proto.Type):
+ return RelationType(type_proto.label, type_proto.root)
+
+ def as_remote(self, transaction):
+ return RemoteRelationType(transaction, self.get_label(), self.is_root())
+
+ def is_relation_type(self):
+ return True
+
+
+class RemoteRelationType(RemoteThingType):
+
+ def as_remote(self, transaction):
+ return RemoteRelationType(transaction, self.get_label(), self.is_root())
+
+ def create(self):
+ method = concept_proto.Type.Req()
+ create_req = concept_proto.RelationType.Create.Req()
+ method.relation_type_create_req.CopyFrom(create_req)
+ return Relation._of(self._execute(method).relation_type_create_res.relation)
+
+ def get_relates(self, role_label: str = None):
+ method = concept_proto.Type.Req()
+ if role_label:
+ get_relates_req = concept_proto.RelationType.GetRelatesForRoleLabel.Req()
+ get_relates_req.label = role_label
+ method.relation_type_get_relates_for_role_label_req.CopyFrom(get_relates_req)
+ res = self._execute(method).relation_type_get_relates_for_role_label_res
+ return concept_proto_reader.type_(res.role_type) if res.HasField("role_type") else None
+ else:
+ method.relation_type_get_relates_req.CopyFrom(concept_proto.RelationType.GetRelates.Req())
+ return self._type_stream(method, lambda res: res.relation_type_get_relates_res.roles)
+
+ def set_relates(self, role_label: str, overridden_label: str = None):
+ method = concept_proto.Type.Req()
+ set_relates_req = concept_proto.RelationType.SetRelates.Req()
+ set_relates_req.label = role_label
+ if overridden_label:
+ set_relates_req.overridden_label = overridden_label
+ method.relation_type_set_relates_req.CopyFrom(set_relates_req)
+ self._execute(method)
+
+ def unset_relates(self, role_label: str):
+ method = concept_proto.Type.Req()
+ unset_relates_req = concept_proto.RelationType.UnsetRelates.Req()
+ unset_relates_req.label = role_label
+ method.relation_type_unset_relates_req.CopyFrom(unset_relates_req)
+ self._execute(method)
diff --git a/grakn/concept/type/role_type.py b/grakn/concept/type/role_type.py
new file mode 100644
index 00000000..e6b5e12d
--- /dev/null
+++ b/grakn/concept/type/role_type.py
@@ -0,0 +1,118 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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 typing import Callable, List
+
+import graknprotocol.protobuf.concept_pb2 as concept_proto
+
+from grakn.concept.proto import concept_proto_reader
+from grakn.concept.type.type import Type, RemoteType
+
+
+class RoleType(Type):
+
+ def __init__(self, label: str, scope: str, is_root: bool):
+ super(RoleType, self).__init__(label, is_root)
+ self._scope = scope
+ self._hash = hash((scope, label))
+
+ @staticmethod
+ def _of(type_proto: concept_proto.Type):
+ return RoleType(type_proto.label, type_proto.scope, type_proto.root)
+
+ def get_scope(self):
+ return self._scope
+
+ def get_scoped_label(self):
+ return self.get_scope() + ":" + self.get_label()
+
+ def as_remote(self, transaction):
+ return RemoteRoleType(transaction, self.get_label(), self.get_scope(), self.is_root())
+
+ def is_role_type(self):
+ return True
+
+ def __str__(self):
+ return type(self).__name__ + "[label:" + self.get_scoped_label() + "]"
+
+ def __eq__(self, other):
+ if other is self:
+ return True
+ if not other or type(self) != type(other):
+ return False
+ return self.get_scoped_label() == other.get_scoped_label()
+
+ def __hash__(self):
+ return super(RoleType, self).__hash__()
+
+
+class RemoteRoleType(RemoteType):
+
+ def __init__(self, transaction, label: str, scope: str, is_root: bool):
+ super(RemoteRoleType, self).__init__(transaction, label, is_root)
+ self._scope = scope
+ self._hash = hash((transaction, scope, label))
+
+ def get_scope(self):
+ return self._scope
+
+ def get_scoped_label(self):
+ return self.get_scope() + ":" + self.get_label()
+
+ def as_remote(self, transaction):
+ return RemoteRoleType(transaction, self.get_label(), self.get_scope(), self.is_root())
+
+ def get_relation_type(self):
+ method = concept_proto.Type.Req()
+ method.role_type_get_relation_type_req.CopyFrom(concept_proto.RoleType.GetRelationType.Req())
+ return concept_proto_reader.type_(self._execute(method).role_type_get_relation_type_res.relation_type)
+
+ def get_relation_types(self):
+ method = concept_proto.Type.Req()
+ method.role_type_get_relation_types_req.CopyFrom(concept_proto.RoleType.GetRelationTypes.Req())
+ return self._type_stream(method, lambda res: res.role_type_get_relation_types_res.relation_types)
+
+ def get_players(self):
+ method = concept_proto.Type.Req()
+ method.role_type_get_players_req.CopyFrom(concept_proto.RoleType.GetPlayers.Req())
+ return self._type_stream(method, lambda res: res.role_type_get_players_res.thing_types)
+
+ def is_role_type(self):
+ return True
+
+ def _type_stream(self, method: concept_proto.Type.Req, type_list_getter: Callable[[concept_proto.Type.Res], List[concept_proto.Type]]):
+ method.scope = self.get_scope()
+ return super(RemoteRoleType, self)._type_stream(method, type_list_getter)
+
+ def _execute(self, method: concept_proto.Type.Req):
+ method.scope = self.get_scope()
+ return super(RemoteRoleType, self)._execute(method)
+
+ def __str__(self):
+ return type(self).__name__ + "[label:" + self.get_scoped_label() + "]"
+
+ def __eq__(self, other):
+ if other is self:
+ return True
+ if not other or type(self) != type(other):
+ return False
+ return self.get_scoped_label() == other.get_scoped_label()
+
+ def __hash__(self):
+ return super(RemoteRoleType, self).__hash__()
diff --git a/grakn/concept/type/thing_type.py b/grakn/concept/type/thing_type.py
new file mode 100644
index 00000000..87608265
--- /dev/null
+++ b/grakn/concept/type/thing_type.py
@@ -0,0 +1,101 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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 graknprotocol.protobuf.concept_pb2 as concept_proto
+
+from grakn.concept.proto import concept_proto_builder
+from grakn.concept.type.type import Type, RemoteType
+
+
+class ThingType(Type):
+
+ def as_remote(self, transaction):
+ return RemoteThingType(transaction, self.get_label(), self.is_root())
+
+
+class RemoteThingType(RemoteType):
+
+ def get_instances(self):
+ method = concept_proto.Type.Req()
+ get_instances_req = concept_proto.ThingType.GetInstances.Req()
+ method.thing_type_get_instances_req.CopyFrom(get_instances_req)
+ return self._thing_stream(method, lambda res: res.thing_type_get_instances_res.things)
+
+ def set_abstract(self):
+ req = concept_proto.Type.Req()
+ req.thing_type_set_abstract_req.CopyFrom(concept_proto.ThingType.SetAbstract.Req())
+ self._execute(req)
+
+ def unset_abstract(self):
+ req = concept_proto.Type.Req()
+ req.thing_type_unset_abstract_req.CopyFrom(concept_proto.ThingType.UnsetAbstract.Req())
+ self._execute(req)
+
+ def set_plays(self, role, overridden_role=None):
+ req = concept_proto.Type.Req()
+ set_plays_req = concept_proto.ThingType.SetPlays.Req()
+ set_plays_req.role.CopyFrom(concept_proto_builder.type_(role))
+ if overridden_role:
+ set_plays_req.overridden_role.CopyFrom(concept_proto_builder.type_(overridden_role))
+ req.thing_type_set_plays_req.CopyFrom(set_plays_req)
+ self._execute(req)
+
+ def set_owns(self, attribute_type, overridden_type=None, is_key=False):
+ req = concept_proto.Type.Req()
+ set_owns_req = concept_proto.ThingType.SetOwns.Req()
+ set_owns_req.attribute_type.CopyFrom(concept_proto_builder.type_(attribute_type))
+ set_owns_req.is_key = is_key
+ if overridden_type:
+ set_owns_req.overridden_type.CopyFrom(concept_proto_builder.type_(overridden_type))
+ req.thing_type_set_owns_req.CopyFrom(set_owns_req)
+ self._execute(req)
+
+ def get_plays(self):
+ req = concept_proto.Type.Req()
+ req.thing_type_get_plays_req.CopyFrom(concept_proto.ThingType.GetPlays.Req())
+ return self._type_stream(req, lambda res: res.thing_type_get_plays_res.roles)
+
+ def get_owns(self, value_type=None, keys_only=False):
+ req = concept_proto.Type.Req()
+ get_owns_req = concept_proto.ThingType.GetOwns.Req()
+ get_owns_req.keys_only = keys_only
+ if value_type:
+ get_owns_req.value_type = concept_proto_builder.value_type(value_type)
+ req.thing_type_get_owns_req.CopyFrom(get_owns_req)
+ return self._type_stream(req, lambda res: res.thing_type_get_owns_res.attribute_types)
+
+ def unset_plays(self, role):
+ req = concept_proto.Type.Req()
+ unset_plays_req = concept_proto.ThingType.UnsetPlays.Req()
+ unset_plays_req.role.CopyFrom(concept_proto_builder.type_(role))
+ req.thing_type_unset_plays_req.CopyFrom(unset_plays_req)
+ self._execute(req)
+
+ def unset_owns(self, attribute_type):
+ req = concept_proto.Type.Req()
+ unset_owns_req = concept_proto.ThingType.UnsetOwns.Req()
+ unset_owns_req.attribute_type.CopyFrom(concept_proto_builder.type_(attribute_type))
+ req.thing_type_unset_owns_req.CopyFrom(unset_owns_req)
+ self._execute(req)
+
+ def as_remote(self, transaction):
+ return RemoteThingType(transaction, self.get_label(), self.is_root())
+
+ def is_thing_type(self):
+ return True
diff --git a/grakn/concept/type/type.py b/grakn/concept/type/type.py
new file mode 100644
index 00000000..5d1f9900
--- /dev/null
+++ b/grakn/concept/type/type.py
@@ -0,0 +1,188 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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 typing import Callable, List
+
+import graknprotocol.protobuf.concept_pb2 as concept_proto
+import graknprotocol.protobuf.transaction_pb2 as transaction_proto
+
+from grakn.common.exception import GraknClientException
+from grakn.concept.proto import concept_proto_builder, concept_proto_reader
+from grakn.concept.concept import Concept, RemoteConcept
+
+
+class Type(Concept):
+
+ def __init__(self, label: str, is_root: bool):
+ if not label:
+ raise GraknClientException("Label must be a non-empty string.")
+ self._label = label
+ self._is_root = is_root
+ self._hash = hash(label)
+
+ def get_label(self):
+ return self._label
+
+ def is_root(self):
+ return self._is_root
+
+ def is_type(self):
+ return True
+
+ def is_thing_type(self):
+ return False
+
+ def is_entity_type(self):
+ return False
+
+ def is_attribute_type(self):
+ return False
+
+ def is_relation_type(self):
+ return False
+
+ def is_role_type(self):
+ return False
+
+ def __str__(self):
+ return type(self).__name__ + "[label:" + self.get_label() + "]"
+
+ def __eq__(self, other):
+ if other is self:
+ return True
+ if not other or type(self) != type(other):
+ return False
+ return self.get_label() == other.get_label()
+
+ def __hash__(self):
+ return self._hash
+
+
+class RemoteType(RemoteConcept):
+
+ def __init__(self, transaction, label: str, is_root: bool):
+ if not transaction:
+ raise GraknClientException("Transaction must be set.")
+ if not label:
+ raise GraknClientException("Label must be a non-empty string.")
+ self._transaction = transaction
+ self._label = label
+ self._is_root = is_root
+ self._hash = hash((self._transaction, label))
+
+ def get_label(self):
+ return self._label
+
+ def is_root(self):
+ return self._is_root
+
+ def set_label(self, label: str):
+ req = concept_proto.Type.Req()
+ set_label_req = concept_proto.Type.SetLabel.Req()
+ set_label_req.label = label
+ req.type_set_label_req.CopyFrom(set_label_req)
+ self._execute(req)
+ self._label = label
+ self._hash = hash((self._transaction, label))
+
+ def is_abstract(self):
+ req = concept_proto.Type.Req()
+ req.type_is_abstract_req.CopyFrom(concept_proto.Type.IsAbstract.Req())
+ res = self._execute(req)
+ return res.type_is_abstract_res.abstract
+
+ def is_type(self):
+ return True
+
+ def is_thing_type(self):
+ return False
+
+ def is_entity_type(self):
+ return False
+
+ def is_attribute_type(self):
+ return False
+
+ def is_relation_type(self):
+ return False
+
+ def is_role_type(self):
+ return False
+
+ def set_supertype(self, _type: Type):
+ req = concept_proto.Type.Req()
+ supertype_req = concept_proto.Type.SetSupertype.Req()
+ supertype_req.type.CopyFrom(concept_proto_builder.type_(_type))
+ req.type_set_supertype_req.CopyFrom(supertype_req)
+ self._execute(req)
+
+ def get_supertype(self):
+ req = concept_proto.Type.Req()
+ req.type_get_supertype_req.CopyFrom(concept_proto.Type.GetSupertype.Req())
+ res = self._execute(req).type_get_supertype_res
+ return concept_proto_reader.type_(res.type) if res.WhichOneof("res") == "type" else None
+
+ def get_supertypes(self):
+ method = concept_proto.Type.Req()
+ method.type_get_supertypes_req.CopyFrom(concept_proto.Type.GetSupertypes.Req())
+ return self._type_stream(method, lambda res: res.type_get_supertypes_res.types)
+
+ def get_subtypes(self):
+ method = concept_proto.Type.Req()
+ method.type_get_subtypes_req.CopyFrom(concept_proto.Type.GetSubtypes.Req())
+ return self._type_stream(method, lambda res: res.type_get_subtypes_res.types)
+
+ def delete(self):
+ method = concept_proto.Type.Req()
+ method.type_delete_req.CopyFrom(concept_proto.Type.Delete.Req())
+ self._execute(method)
+
+ def is_deleted(self):
+ return not self._transaction.concepts().get_type(self.get_label())
+
+ def _type_stream(self, method: concept_proto.Type.Req, type_list_getter: Callable[[concept_proto.Type.Res], List[concept_proto.Type]]):
+ method.label = self.get_label()
+ request = transaction_proto.Transaction.Req()
+ request.type_req.CopyFrom(method)
+ return map(lambda type_proto: concept_proto_reader.type_(type_proto), self._transaction._stream(request, lambda res: type_list_getter(res.type_res)))
+
+ def _thing_stream(self, method: concept_proto.Type.Req, thing_list_getter: Callable[[concept_proto.Type.Res], List[concept_proto.Thing]]):
+ method.label = self.get_label()
+ request = transaction_proto.Transaction.Req()
+ request.type_req.CopyFrom(method)
+ return map(lambda thing_proto: concept_proto_reader.thing(thing_proto), self._transaction._stream(request, lambda res: thing_list_getter(res.type_res)))
+
+ def _execute(self, method: concept_proto.Type.Req):
+ method.label = self.get_label()
+ request = transaction_proto.Transaction.Req()
+ request.type_req.CopyFrom(method)
+ return self._transaction._execute(request).type_res
+
+ def __str__(self):
+ return type(self).__name__ + "[label:" + self.get_label() + "]"
+
+ def __eq__(self, other):
+ if other is self:
+ return True
+ if not other or type(self) != type(other):
+ return False
+ return self._transaction is other._transaction and self.get_label() == other.get_label()
+
+ def __hash__(self):
+ return self._hash
diff --git a/grakn/concept/type/value_type.py b/grakn/concept/type/value_type.py
new file mode 100644
index 00000000..e6ad39f9
--- /dev/null
+++ b/grakn/concept/type/value_type.py
@@ -0,0 +1,30 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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 enum
+
+
+# This lives here to avoid circular imports.
+class ValueType(enum.Enum):
+ OBJECT = 0
+ BOOLEAN = 1
+ LONG = 2
+ DOUBLE = 3
+ STRING = 4
+ DATETIME = 5
diff --git a/grakn/grakn_proto_builder.py b/grakn/grakn_proto_builder.py
new file mode 100644
index 00000000..cec9e89c
--- /dev/null
+++ b/grakn/grakn_proto_builder.py
@@ -0,0 +1,33 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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 graknprotocol.protobuf.options_pb2 as options_proto
+
+from grakn.options import GraknOptions
+
+
+def options(opts: GraknOptions):
+ proto_options = options_proto.Options()
+ if opts.infer is not None:
+ proto_options.infer = opts.infer
+ if opts.explain is not None:
+ proto_options.explain = opts.explain
+ if opts.batch_size is not None:
+ proto_options.batch_size = opts.batch_size
+ return proto_options
diff --git a/grakn/logic/logic_manager.py b/grakn/logic/logic_manager.py
new file mode 100644
index 00000000..fa832c6d
--- /dev/null
+++ b/grakn/logic/logic_manager.py
@@ -0,0 +1,53 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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 graknprotocol.protobuf.logic_pb2 as logic_proto
+import graknprotocol.protobuf.transaction_pb2 as transaction_proto
+
+from grakn.logic.rule import Rule
+
+
+class LogicManager(object):
+
+ def __init__(self, transaction):
+ self._transaction = transaction
+
+ def put_rule(self, label: str, when: str, then: str):
+ req = logic_proto.LogicManager.Req()
+ put_rule_req = logic_proto.LogicManager.PutRule.Req()
+ put_rule_req.label = label
+ put_rule_req.when = when
+ put_rule_req.then = then
+ req.put_rule_req.CopyFrom(put_rule_req)
+ res = self._execute(req)
+ return Rule._of(res.put_rule_res.rule)
+
+ def get_rule(self, label: str):
+ req = logic_proto.LogicManager.Req()
+ get_rule_req = logic_proto.LogicManager.GetRule.Req()
+ get_rule_req.label = label
+ req.get_rule_req.CopyFrom(get_rule_req)
+
+ response = self._execute(req)
+ return Rule._of(response.get_rule_res.rule) if response.get_rule_res.WhichOneof("res") == "rule" else None
+
+ def _execute(self, request: logic_proto.LogicManager.Req):
+ req = transaction_proto.Transaction.Req()
+ req.logic_manager_req.CopyFrom(request)
+ return self._transaction._execute(req).logic_manager_res
diff --git a/grakn/logic/rule.py b/grakn/logic/rule.py
new file mode 100644
index 00000000..42d561d1
--- /dev/null
+++ b/grakn/logic/rule.py
@@ -0,0 +1,111 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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 graknprotocol.protobuf.logic_pb2 as logic_proto
+import graknprotocol.protobuf.transaction_pb2 as transaction_proto
+
+from grakn.common.exception import GraknClientException
+
+
+class Rule(object):
+
+ def __init__(self, label: str, when: str, then: str):
+ if not label:
+ raise GraknClientException("Label must be a non-empty string.")
+ self._label = label
+ self._when = when
+ self._then = then
+ self._hash = hash(label)
+
+ @staticmethod
+ def _of(rule_proto: logic_proto.Rule):
+ return Rule(rule_proto.label, rule_proto.when, rule_proto.then)
+
+ def get_label(self):
+ return self._label
+
+ def get_when(self):
+ return self._when
+
+ def get_then(self):
+ return self._then
+
+ def as_remote(self, transaction):
+ return RemoteRule(transaction, self.get_label(), self.get_when(), self.get_then())
+
+ def is_remote(self):
+ return False
+
+ def __str__(self):
+ return type(self).__name__ + "[label:" + self.get_label() + "]"
+
+ def __eq__(self, other):
+ if other is self:
+ return True
+ if not other or type(self) != type(other):
+ return False
+ return self.get_label() == other.get_label()
+
+ def __hash__(self):
+ return self._hash
+
+
+class RemoteRule(Rule):
+
+ def __init__(self, transaction, label: str, when: str, then: str):
+ super(RemoteRule, self).__init__(label, when, then)
+ if not transaction:
+ raise GraknClientException("Transaction must be set.")
+ self._transaction = transaction
+ self._hash = hash((transaction, label))
+
+ def set_label(self, label: str):
+ req = logic_proto.Rule.Req()
+ set_label_req = logic_proto.Rule.SetLabel.Req()
+ set_label_req.label = label
+ req.rule_set_label_req.CopyFrom(set_label_req)
+ self._execute(req)
+ self._label = label
+
+ def delete(self):
+ method = logic_proto.Rule.Req()
+ method.rule_delete_req.CopyFrom(logic_proto.Rule.Delete.Req())
+ self._execute(method)
+
+ def is_deleted(self):
+ return not self._transaction.logic().get_rule(self.get_label())
+
+ def is_remote(self):
+ return True
+
+ def __eq__(self, other):
+ if other is self:
+ return True
+ if not other or type(self) != type(other):
+ return False
+ return self._transaction is other._transaction and self.get_label() == other.get_label()
+
+ def __hash__(self):
+ return super(RemoteRule, self).__hash__()
+
+ def _execute(self, method: logic_proto.Rule.Req):
+ method.label = self.get_label()
+ request = transaction_proto.Transaction.Req()
+ request.rule_req.CopyFrom(method)
+ return self._transaction._execute(request).rule_res
diff --git a/grakn/options.py b/grakn/options.py
new file mode 100644
index 00000000..4b6229ae
--- /dev/null
+++ b/grakn/options.py
@@ -0,0 +1,26 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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.
+#
+
+
+class GraknOptions(object):
+
+ def __init__(self):
+ self.infer = None
+ self.explain = None
+ self.batch_size = None
diff --git a/grakn/query/query_manager.py b/grakn/query/query_manager.py
new file mode 100644
index 00000000..0428fdd6
--- /dev/null
+++ b/grakn/query/query_manager.py
@@ -0,0 +1,81 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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 typing import Callable, List
+
+import graknprotocol.protobuf.query_pb2 as query_proto
+import graknprotocol.protobuf.transaction_pb2 as transaction_proto
+
+from grakn import grakn_proto_builder
+from grakn.concept.answer import concept_map
+from grakn.options import GraknOptions
+
+
+class QueryManager(object):
+
+ def __init__(self, transaction):
+ self._transaction = transaction
+
+ def match(self, query: str, options=GraknOptions()):
+ request = query_proto.Query.Req()
+ match_req = query_proto.Graql.Match.Req()
+ match_req.query = query
+ request.match_req.CopyFrom(match_req)
+ return map(lambda answer_proto: concept_map._of(answer_proto), self._iterate_query(request, lambda res: res.query_res.match_res.answers, options))
+
+ def insert(self, query: str, options=GraknOptions()):
+ request = query_proto.Query.Req()
+ insert_req = query_proto.Graql.Insert.Req()
+ insert_req.query = query
+ request.insert_req.CopyFrom(insert_req)
+ return map(lambda answer_proto: concept_map._of(answer_proto), self._iterate_query(request, lambda res: res.query_res.insert_res.answers, options))
+
+ def delete(self, query: str, options=GraknOptions()):
+ request = query_proto.Query.Req()
+ delete_req = query_proto.Graql.Delete.Req()
+ delete_req.query = query
+ request.delete_req.CopyFrom(delete_req)
+ self._run_query(request, options)
+
+ def define(self, query: str, options=GraknOptions()):
+ request = query_proto.Query.Req()
+ define_req = query_proto.Graql.Define.Req()
+ define_req.query = query
+ request.define_req.CopyFrom(define_req)
+ self._run_query(request, options)
+
+ def undefine(self, query: str, options=GraknOptions()):
+ request = query_proto.Query.Req()
+ undefine_req = query_proto.Graql.Undefine.Req()
+ undefine_req.query = query
+ request.undefine_req.CopyFrom(undefine_req)
+ self._run_query(request, options)
+
+ def _run_query(self, query_req: query_proto.Query.Req, options: GraknOptions):
+ req = transaction_proto.Transaction.Req()
+ query_req.options.CopyFrom(grakn_proto_builder.options(options))
+ req.query_req.CopyFrom(query_req)
+ # Using stream makes this request asynchronous.
+ self._transaction._stream(req)
+
+ def _iterate_query(self, query_req: query_proto.Query.Req, response_reader: Callable[[transaction_proto.Transaction.Res], List], options: GraknOptions):
+ req = transaction_proto.Transaction.Req()
+ query_req.options.CopyFrom(grakn_proto_builder.options(options))
+ req.query_req.CopyFrom(query_req)
+ return self._transaction._stream(req, response_reader)
diff --git a/grakn/rpc/Transaction.py b/grakn/rpc/Transaction.py
new file mode 100644
index 00000000..63b634b3
--- /dev/null
+++ b/grakn/rpc/Transaction.py
@@ -0,0 +1,194 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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 enum
+from typing import Callable, List
+
+import grpc
+import six
+import time
+import uuid
+
+from six.moves import queue
+
+from graknprotocol.protobuf.grakn_pb2_grpc import GraknStub
+import graknprotocol.protobuf.transaction_pb2 as transaction_proto
+
+from grakn import grakn_proto_builder
+from grakn.common.exception import GraknClientException
+from grakn.concept.concept_manager import ConceptManager
+from grakn.options import GraknOptions
+from grakn.query.query_manager import QueryManager
+from grakn.rpc.stream import Stream
+from grakn.logic.logic_manager import LogicManager
+
+
+class TransactionType(enum.Enum):
+ READ = 0
+ WRITE = 1
+
+
+class Transaction(object):
+
+ def __init__(self, channel: grpc.Channel, session_id: str, transaction_type: TransactionType, options=GraknOptions()):
+ self._transaction_type = transaction_type
+ self._concept_manager = ConceptManager(self)
+ self._query_manager = QueryManager(self)
+ self._logic_manager = LogicManager(self)
+ self._response_queues = {}
+
+ self._grpc_stub = GraknStub(channel)
+ self._request_iterator = RequestIterator()
+ self._response_iterator = self._grpc_stub.transaction(self._request_iterator)
+ self._transaction_was_closed = False
+
+ open_req = transaction_proto.Transaction.Open.Req()
+ open_req.session_id = session_id
+ open_req.type = Transaction._transaction_type_proto(transaction_type)
+ open_req.options.CopyFrom(grakn_proto_builder.options(options))
+ req = transaction_proto.Transaction.Req()
+ req.open_req.CopyFrom(open_req)
+
+ start_time = time.time() * 1000.0
+ res = self._execute(req)
+ end_time = time.time() * 1000.0
+ self._network_latency_millis = end_time - start_time - res.open_res.processing_time_millis
+
+ def transaction_type(self):
+ return self._transaction_type
+
+ def is_open(self):
+ return not self._transaction_was_closed
+
+ def concepts(self):
+ return self._concept_manager
+
+ def query(self):
+ return self._query_manager
+
+ def logic(self):
+ return self._logic_manager
+
+ def commit(self):
+ req = transaction_proto.Transaction.Req()
+ commit_req = transaction_proto.Transaction.Commit.Req()
+ req.commit_req.CopyFrom(commit_req)
+ self._execute(req)
+
+ def rollback(self):
+ req = transaction_proto.Transaction.Req()
+ rollback_req = transaction_proto.Transaction.Rollback.Req()
+ req.rollback_req.CopyFrom(rollback_req)
+ self._execute(req)
+
+ def close(self):
+ self._transaction_was_closed = True
+ self._request_iterator.close()
+
+ def _execute(self, request: transaction_proto.Transaction.Req):
+ response_queue = queue.Queue()
+ request_id = str(uuid.uuid4())
+ request.id = request_id
+ if self._transaction_was_closed:
+ raise GraknClientException("The transaction has been closed and no further operation is allowed.")
+ self._response_queues[request_id] = response_queue
+ self._request_iterator.put(request)
+ return self._fetch(request_id)
+
+ def _stream(self, request: transaction_proto.Transaction.Req, transform_response: Callable[[transaction_proto.Transaction.Res], List] = None):
+ response_queue = queue.Queue()
+ request_id = str(uuid.uuid4())
+ request.id = request_id
+ if self._transaction_was_closed:
+ raise GraknClientException("The transaction has been closed and no further operation is allowed.")
+ self._response_queues[request_id] = response_queue
+ self._request_iterator.put(request)
+ return Stream(self, request_id, transform_response)
+
+ def _fetch(self, request_id: str):
+ try:
+ return self._response_queues[request_id].get(block=False)
+ except queue.Empty:
+ pass
+
+ # Keep taking responses until we get one that matches the request ID
+ while True:
+ try:
+ response = next(self._response_iterator)
+ except grpc.RpcError as e:
+ self._transaction_was_closed = True
+ grakn_exception = GraknClientException(e.details())
+ for response_queue in self._response_queues.values():
+ response_queue.put(grakn_exception)
+ # noinspection PyUnresolvedReferences
+ raise grakn_exception
+ except StopIteration:
+ raise GraknClientException("The transaction has been closed and no further operation is allowed.")
+
+ if isinstance(response, GraknClientException):
+ raise response
+ elif response.id == request_id:
+ return response
+ else:
+ response_queue = self._response_queues[response.id]
+ if response_queue is None:
+ raise GraknClientException("Received a response with unknown request id '" + response.id + "'.")
+ response_queue.put(response)
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, exc_type, exc_val, exc_tb):
+ self.close()
+ if exc_tb is None:
+ pass
+ else:
+ return False
+
+ @staticmethod
+ def _transaction_type_proto(transaction_type):
+ if transaction_type == TransactionType.READ:
+ return transaction_proto.Transaction.Type.Value("READ")
+ if transaction_type == TransactionType.WRITE:
+ return transaction_proto.Transaction.Type.Value("WRITE")
+
+
+class RequestIterator(six.Iterator):
+ CLOSE_STREAM = "CLOSE_STREAM"
+
+ def __init__(self):
+ self._request_queue = queue.Queue()
+
+ def __iter__(self):
+ return self
+
+ # Essentially the gRPC stream is constantly polling this iterator. When we issue a new request, it gets put into
+ # the back of the queue and gRPC will pick it up when it gets round to it (this is usually instantaneous)
+ def __next__(self):
+ request = self._request_queue.get(block=True)
+ if request is RequestIterator.CLOSE_STREAM:
+ # Close the stream.
+ raise StopIteration()
+ return request
+
+ def put(self, request: transaction_proto.Transaction.Req):
+ self._request_queue.put(request)
+
+ def close(self):
+ self._request_queue.put(RequestIterator.CLOSE_STREAM)
diff --git a/grakn/rpc/database_manager.py b/grakn/rpc/database_manager.py
new file mode 100644
index 00000000..45ec2a6c
--- /dev/null
+++ b/grakn/rpc/database_manager.py
@@ -0,0 +1,46 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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 graknprotocol.protobuf.grakn_pb2_grpc import GraknStub
+import graknprotocol.protobuf.database_pb2 as database_proto
+from grpc import Channel
+
+
+class DatabaseManager(object):
+
+ def __init__(self, channel: Channel):
+ self._grpc_stub = GraknStub(channel)
+
+ def contains(self, name: str):
+ request = database_proto.Database.Contains.Req()
+ request.name = name
+ return self._grpc_stub.database_contains(request).contains
+
+ def create(self, name: str):
+ request = database_proto.Database.Create.Req()
+ request.name = name
+ self._grpc_stub.database_create(request)
+
+ def delete(self, name: str):
+ request = database_proto.Database.Delete.Req()
+ request.name = name
+ self._grpc_stub.database_delete(request)
+
+ def all(self):
+ return list(self._grpc_stub.database_all(database_proto.Database.All.Req()).names)
diff --git a/grakn/rpc/session.py b/grakn/rpc/session.py
new file mode 100644
index 00000000..6ee13efa
--- /dev/null
+++ b/grakn/rpc/session.py
@@ -0,0 +1,92 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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 graknprotocol.protobuf.grakn_pb2_grpc import GraknStub
+import graknprotocol.protobuf.session_pb2 as session_proto
+import enum
+
+from grakn import grakn_proto_builder
+from grakn.options import GraknOptions
+from grakn.rpc.transaction import Transaction, TransactionType
+
+
+class SessionType(enum.Enum):
+ DATA = 0
+ SCHEMA = 1
+
+
+class Session(object):
+
+ def __init__(self, client, database: str, session_type: SessionType, options=GraknOptions()):
+ self._channel = client._channel
+ self._scheduler = client._scheduler
+ self._database = database
+ self._session_type = session_type
+ self._grpc_stub = GraknStub(self._channel)
+
+ open_req = session_proto.Session.Open.Req()
+ open_req.database = database
+ open_req.type = Session._session_type_proto(session_type)
+ open_req.options.CopyFrom(grakn_proto_builder.options(options))
+
+ self._session_id = self._grpc_stub.session_open(open_req).session_id
+ self._is_open = True
+ self._pulse = self._scheduler.enter(5, 1, self._transmit_pulse, ())
+
+ def transaction(self, transaction_type: TransactionType, options=GraknOptions()):
+ return Transaction(self._channel, self._session_id, transaction_type, options)
+
+ def session_type(self): return self._session_type
+
+ def is_open(self): return self._is_open
+
+ def close(self):
+ if self._is_open:
+ self._is_open = False
+ req = session_proto.Session.Close.Req()
+ req.session_id = self._session_id
+ self._grpc_stub.session_close(req)
+
+ def database(self): return self._database
+
+ def _transmit_pulse(self):
+ if not self._is_open:
+ return
+ pulse_req = session_proto.Session.Pulse.Req()
+ pulse_req.session_id = self._session_id
+ is_alive = self._grpc_stub.session_pulse(pulse_req).is_alive
+ if is_alive:
+ self._pulse = self._scheduler.enter(5, 1, self._transmit_pulse, ())
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, exc_type, exc_val, exc_tb):
+ self.close()
+ if exc_tb is None:
+ pass
+ else:
+ return False
+
+ @staticmethod
+ def _session_type_proto(session_type: SessionType):
+ if session_type == SessionType.DATA:
+ return session_proto.Session.Type.Value("DATA")
+ if session_type == SessionType.SCHEMA:
+ return session_proto.Session.Type.Value("SCHEMA")
diff --git a/grakn/rpc/stream.py b/grakn/rpc/stream.py
new file mode 100644
index 00000000..0fb214c4
--- /dev/null
+++ b/grakn/rpc/stream.py
@@ -0,0 +1,64 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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 typing import Callable, List
+
+import six
+
+import graknprotocol.protobuf.transaction_pb2 as transaction_proto
+
+from grakn.common.exception import GraknClientException
+
+
+class Stream(six.Iterator):
+
+ _CONTINUE = "continue"
+ _DONE = "done"
+
+ def __init__(self, transaction, request_id: str, transform_response: Callable[[transaction_proto.Transaction.Res], List] = None):
+ self._transaction = transaction
+ self._request_id = request_id
+ self._transform_response = transform_response
+ self._current_iterator = None
+
+ def __iter__(self):
+ return self
+
+ def __next__(self):
+ if self._current_iterator is not None:
+ try:
+ return next(self._current_iterator)
+ except StopIteration:
+ self._current_iterator = None
+
+ res = self._transaction._fetch(self._request_id)
+ res_case = res.WhichOneof("res")
+ if res_case == Stream._CONTINUE:
+ continue_req = transaction_proto.Transaction.Req()
+ continue_req.id = self._request_id
+ setattr(continue_req, "continue", True)
+ self._transaction._request_iterator.put(continue_req)
+ return next(self)
+ elif res_case == Stream._DONE:
+ raise StopIteration()
+ elif res_case is None:
+ raise GraknClientException("The required field 'res' of type 'transaction_proto.Transaction.Res' was not set.")
+ else:
+ self._current_iterator = iter(self._transform_response(res))
+ return next(self)
diff --git a/grakn/service/Keyspace/KeyspaceService.py b/grakn/service/Keyspace/KeyspaceService.py
deleted file mode 100644
index 57cd9398..00000000
--- a/grakn/service/Keyspace/KeyspaceService.py
+++ /dev/null
@@ -1,46 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you 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 grakn_protocol.keyspace.Keyspace_pb2_grpc import KeyspaceServiceStub
-import grakn_protocol.keyspace.Keyspace_pb2 as keyspace_messages
-
-
-class KeyspaceService(object):
-
- def __init__(self, uri, channel, credentials=None):
- self.uri = uri
- self.stub = KeyspaceServiceStub(channel)
- self.credentials = credentials
-
- def retrieve(self):
- retrieve_request = keyspace_messages.Keyspace.Retrieve.Req()
- if self.credentials:
- retrieve_request.username = self.credentials['username']
- retrieve_request.password = self.credentials['password']
- response = self.stub.retrieve(retrieve_request)
- return list(response.names)
-
- def delete(self, keyspace):
- delete_request = keyspace_messages.Keyspace.Delete.Req()
- delete_request.name = keyspace
- if self.credentials:
- delete_request.username = self.credentials['username']
- delete_request.password = self.credentials['password']
- self.stub.delete(delete_request)
- return
diff --git a/grakn/service/Session/Concept/BaseTypeMapping.py b/grakn/service/Session/Concept/BaseTypeMapping.py
deleted file mode 100644
index a496e117..00000000
--- a/grakn/service/Session/Concept/BaseTypeMapping.py
+++ /dev/null
@@ -1,56 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you 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 grakn_protocol.session.Concept_pb2 as ConceptMessages
-
-
-# base type constant names
-CONCEPTS = META_TYPE, ATTRIBUTE_TYPE, RELATION_TYPE, ENTITY_TYPE, ENTITY, ATTRIBUTE, RELATION, ROLE, RULE = \
- "META_TYPE", "ATTRIBUTE_TYPE", "RELATION_TYPE", "ENTITY_TYPE", "ENTITY", "ATTRIBUTE", "RELATION", "ROLE", "RULE"
-
-"""
-NOTE: the string META_TYPE is the name of the programmatic type of
-Thing, Entity, Attribute, Relation IN GRPC. In the original Java server implementation,
-these have the type TYPE, but due to bad naming at some point, GRPC
-says the base_type of them is META_TYPE. Thus, there will never be
-any concepts with base_type TYPE, only META_TYPE on GRPC-connected clients.
-To match the server class hierarchy, I here instantiate TYPE objects rather than
-META_TYPE, and when the time comes we will rename META_TYPE to TYPE on GRPC connected
-clients too.
-"""
-
-grpc_base_types = ConceptMessages.Concept.BASE_TYPE
-grpc_base_type_to_name = {
- grpc_base_types.Value("META_TYPE"): META_TYPE,
- grpc_base_types.Value("ENTITY_TYPE"): ENTITY_TYPE,
- grpc_base_types.Value("RELATION_TYPE"): RELATION_TYPE,
- grpc_base_types.Value("ATTRIBUTE_TYPE"): ATTRIBUTE_TYPE,
- grpc_base_types.Value("ROLE"): ROLE,
- grpc_base_types.Value("RULE"): RULE,
- grpc_base_types.Value("ENTITY"): ENTITY,
- grpc_base_types.Value("RELATION"): RELATION,
- grpc_base_types.Value("ATTRIBUTE"): ATTRIBUTE
-}
-
-# reverse lookup of above
-# note: assuming one-to-one correspondence
-name_to_grpc_base_type = dict(zip(grpc_base_type_to_name.values(), grpc_base_type_to_name.keys()))
-
-
-
diff --git a/grakn/service/Session/Concept/Concept.py b/grakn/service/Session/Concept/Concept.py
deleted file mode 100644
index f8858bec..00000000
--- a/grakn/service/Session/Concept/Concept.py
+++ /dev/null
@@ -1,196 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you 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 grakn.service.Session.Concept import BaseTypeMapping
-
-
-def base_type_of(concept):
- return object_to_name[type(concept)]
-
-
-class Concept(object):
-
- def __init__(self, grpc_concept):
- self.id = grpc_concept.id
- self.base_type = base_type_of(self)
-
- def as_remote(self, tx):
- from grakn.service.Session.Concept import ConceptFactory
- return ConceptFactory.create_remote_concept_base(tx._tx_service, self.id, BaseTypeMapping.name_to_grpc_base_type[base_type_of(self)])
-
- def is_schema_concept(self):
- """ Check if this concept is a schema concept """
- return isinstance(self, SchemaConcept)
-
- is_schema_concept.__annotations__ = {'return': bool}
-
- def is_type(self):
- """ Check if this concept is a Type concept """
- return isinstance(self, Type)
-
- is_type.__annotations__ = {'return': bool}
-
- def is_thing(self):
- """ Check if this concept is a Thing concept """
- return isinstance(self, Thing)
-
- is_thing.__annotations__ = {'return': bool}
-
- def is_attribute_type(self):
- """ Check if this concept is an AttributeType concept """
- return isinstance(self, AttributeType)
-
- is_attribute_type.__annotations__ = {'return': bool}
-
- def is_entity_type(self):
- """ Check if this concept is an EntityType concept """
- return isinstance(self, EntityType)
-
- is_entity_type.__annotations__ = {'return': bool}
-
- def is_relation_type(self):
- """ Check if this concept is a RelationType concept """
- return isinstance(self, RelationType)
-
- is_relation_type.__annotations__ = {'return': bool}
-
- def is_role(self):
- """ Check if this concept is a Role """
- return isinstance(self, Role)
-
- is_role.__annotations__ = {'return': bool}
-
- def is_rule(self):
- """ Check if this concept is a Rule concept """
- return isinstance(self, Rule)
-
- is_rule.__annotations__ = {'return': bool}
-
- def is_attribute(self):
- """ Check if this concept is an Attribute concept """
- return isinstance(self, Attribute)
-
- is_attribute.__annotations__ = {'return': bool}
-
- def is_entity(self):
- """ Check if this concept is an Entity concept """
- return isinstance(self, Entity)
-
- is_entity.__annotations__ = {'return': bool}
-
- def is_relation(self):
- """ Check if this concept is a Relation concept """
- return isinstance(self, Relation)
-
- is_relation.__annotations__ = {'return': bool}
-
-
-class SchemaConcept(Concept):
-
- def __init__(self, grpc_concept):
- super(SchemaConcept, self).__init__(grpc_concept)
- self._label = grpc_concept.label_res.label
-
- def label(self):
- """
- Get the label of this schema concept.
- """
- return self._label
-
-
-class Type(SchemaConcept):
- pass
-
-
-class EntityType(Type):
- pass
-
-
-class AttributeType(Type):
-
- def __index__(self, grpc_concept):
- super(Type, self).__init__(grpc_concept)
- from grakn.service.Session.util import ResponseReader
- self._value_type = ResponseReader.ResponseReader.from_grpc_value_type_res(grpc_concept.valueType_res)
-
- def value_type(self):
- """ Get the ValueType enum (grakn.ValueType) corresponding to the type of this attribute """
- return self._value_type
-
-
-class RelationType(Type):
- pass
-
-
-class Rule(SchemaConcept):
- pass
-
-
-class Role(SchemaConcept):
- pass
-
-
-class Thing(Concept):
-
- def __init__(self, grpc_concept):
- super(Thing, self).__init__(grpc_concept)
- self._inferred = grpc_concept.inferred_res.inferred
- from grakn.service.Session.Concept import ConceptFactory
- self._type = ConceptFactory.create_local_concept(grpc_concept.type_res.type)
-
- def is_inferred(self):
- return self._inferred
-
- def type(self):
- return self._type
-
-
-class Entity(Thing):
- pass
-
-
-class Attribute(Thing):
-
- def __init__(self, grpc_concept):
- super(Attribute, self).__init__(grpc_concept)
- from grakn.service.Session.util import ResponseReader
- self._value = ResponseReader.ResponseReader.from_grpc_value_object(grpc_concept.value_res.value)
-
- def value(self):
- return self._value
-
-
-class Relation(Thing):
- pass
-
-
-name_to_object = {
- BaseTypeMapping.META_TYPE: Type,
- BaseTypeMapping.ENTITY_TYPE: EntityType,
- BaseTypeMapping.RELATION_TYPE: RelationType,
- BaseTypeMapping.ATTRIBUTE_TYPE: AttributeType,
- BaseTypeMapping.ROLE: Role,
- BaseTypeMapping.RULE: Rule,
- BaseTypeMapping.ENTITY: Entity,
- BaseTypeMapping.RELATION: Relation,
- BaseTypeMapping.ATTRIBUTE: Attribute
-}
-
-object_to_name = dict(zip(name_to_object.values(), name_to_object.keys()))
diff --git a/grakn/service/Session/Concept/ConceptFactory.py b/grakn/service/Session/Concept/ConceptFactory.py
deleted file mode 100644
index 7717296a..00000000
--- a/grakn/service/Session/Concept/ConceptFactory.py
+++ /dev/null
@@ -1,63 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you 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 grakn.service.Session.Concept import BaseTypeMapping, Concept, RemoteConcept
-
-# map names to ConceptHierarchy types
-name_to_remote_object = {
- BaseTypeMapping.META_TYPE: RemoteConcept.RemoteType,
- BaseTypeMapping.ENTITY_TYPE: RemoteConcept.RemoteEntityType,
- BaseTypeMapping.RELATION_TYPE: RemoteConcept.RemoteRelationType,
- BaseTypeMapping.ATTRIBUTE_TYPE: RemoteConcept.RemoteAttributeType,
- BaseTypeMapping.ROLE: RemoteConcept.RemoteRole,
- BaseTypeMapping.RULE: RemoteConcept.RemoteRule,
- BaseTypeMapping.ENTITY: RemoteConcept.RemoteEntity,
- BaseTypeMapping.RELATION: RemoteConcept.RemoteRelation,
- BaseTypeMapping.ATTRIBUTE: RemoteConcept.RemoteAttribute
-}
-
-
-def create_remote_concept(tx_service, grpc_concept):
-
- concept_id = grpc_concept.id
- base_type = grpc_concept.baseType
-
- return create_remote_concept_base(tx_service, concept_id, base_type)
-
-
-def create_remote_concept_base(tx_service, concept_id, base_type):
- """ Instantate a local Python object for a Concept corresponding to the .baseType of the GRPC concept object """
-
- concept_name = BaseTypeMapping.grpc_base_type_to_name[base_type]
- concept_class = name_to_remote_object[concept_name]
-
- return concept_class(concept_id, concept_name, tx_service)
-
-
-def create_local_concept(grpc_concept):
-
- base_type = grpc_concept.baseType
-
- try:
- concept_name = BaseTypeMapping.grpc_base_type_to_name[base_type]
- concept_class = Concept.name_to_object[concept_name]
- except KeyError as ke:
- raise ke
-
- return concept_class(grpc_concept)
diff --git a/grakn/service/Session/Concept/RemoteConcept.py b/grakn/service/Session/Concept/RemoteConcept.py
deleted file mode 100644
index 8ef32cd2..00000000
--- a/grakn/service/Session/Concept/RemoteConcept.py
+++ /dev/null
@@ -1,562 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you 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 grakn.service.Session.util import enums
-from grakn.service.Session.util.RequestBuilder import RequestBuilder
-from grakn.exception.GraknError import GraknError
-from six.moves import map
-
-
-class RemoteConcept(object):
-
- def __init__(self, concept_id, base_type, tx_service):
- self.id = concept_id
- self.base_type = base_type
- self._tx_service = tx_service
-
- def as_remote(self, tx_service):
- return type(self)(self.id, self.base_type, tx_service)
-
- def delete(self):
- del_request = RequestBuilder.ConceptMethod.delete()
- method_response = self._tx_service.run_concept_method(self.id, del_request)
- return
-
- def is_deleted(self):
- retrieved = self._tx_service.get_concept(self.id)
- return retrieved is None
-
- def is_schema_concept(self):
- """ Check if this concept is a schema concept """
- return isinstance(self, RemoteSchemaConcept)
-
- is_schema_concept.__annotations__ = {'return': bool}
-
- def is_type(self):
- """ Check if this concept is a Type concept """
- return isinstance(self, RemoteType)
-
- is_type.__annotations__ = {'return': bool}
-
- def is_thing(self):
- """ Check if this concept is a Thing concept """
- return isinstance(self, RemoteThing)
-
- is_thing.__annotations__ = {'return': bool}
-
- def is_attribute_type(self):
- """ Check if this concept is an AttributeType concept """
- return isinstance(self, RemoteAttributeType)
-
- is_attribute_type.__annotations__ = {'return': bool}
-
- def is_entity_type(self):
- """ Check if this concept is an EntityType concept """
- return isinstance(self, RemoteEntityType)
-
- is_entity_type.__annotations__ = {'return': bool}
-
- def is_relation_type(self):
- """ Check if this concept is a RelationType concept """
- return isinstance(self, RemoteRelationType)
-
- is_relation_type.__annotations__ = {'return': bool}
-
- def is_role(self):
- """ Check if this concept is a Role """
- return isinstance(self, RemoteRole)
-
- is_role.__annotations__ = {'return': bool}
-
- def is_rule(self):
- """ Check if this concept is a Rule concept """
- return isinstance(self, RemoteRule)
-
- is_rule.__annotations__ = {'return': bool}
-
- def is_attribute(self):
- """ Check if this concept is an Attribute concept """
- return isinstance(self, RemoteAttribute)
-
- is_attribute.__annotations__ = {'return': bool}
-
- def is_entity(self):
- """ Check if this concept is an Entity concept """
- return isinstance(self, RemoteEntity)
-
- is_entity.__annotations__ = {'return': bool}
-
- def is_relation(self):
- """ Check if this concept is a Relation concept """
- return isinstance(self, RemoteRelation)
-
- is_relation.__annotations__ = {'return': bool}
-
-
-class RemoteSchemaConcept(RemoteConcept):
-
- def label(self, value=None):
- """
- Get or set label of this schema concept.
- If used as setter returns self
- """
- if value is None:
- get_label_req = RequestBuilder.ConceptMethod.SchemaConcept.get_label()
- method_response = self._tx_service.run_concept_method(self.id, get_label_req)
- return method_response.schemaConcept_getLabel_res.label
- else:
- set_label_req = RequestBuilder.ConceptMethod.SchemaConcept.set_label(value)
- method_response = self._tx_service.run_concept_method(self.id, set_label_req)
- return self
-
- def sup(self, super_concept=None):
- """
- Get or set super schema concept.
- If used as a setter returns self
- """
- if super_concept is None:
- # get direct super schema concept
- get_sup_req = RequestBuilder.ConceptMethod.SchemaConcept.get_sup()
- method_response = self._tx_service.run_concept_method(self.id, get_sup_req)
- get_sup_response = method_response.schemaConcept_getSup_res
- # check if received a Null or Concept
- whichone = get_sup_response.WhichOneof('res')
- if whichone == 'schemaConcept':
- grpc_schema_concept = get_sup_response.schemaConcept
- from grakn.service.Session.Concept import ConceptFactory
- concept = ConceptFactory.create_remote_concept(self._tx_service, grpc_schema_concept)
- return concept
- elif whichone == 'null':
- return None
- else:
- raise GraknError("Unknown response concent for getting super schema concept: {0}".format(whichone))
- else:
- # set direct super SchemaConcept of this SchemaConcept
- set_sup_req = RequestBuilder.ConceptMethod.SchemaConcept.set_sup(super_concept)
- method_response = self._tx_service.run_concept_method(self.id, set_sup_req)
- return self
-
- def subs(self):
- """ Retrieve the sub schema concepts of this schema concept, as an iterator """
- subs_req = RequestBuilder.ConceptMethod.SchemaConcept.subs()
- from grakn.service.Session.Concept import ConceptFactory
- return map(lambda iter_res:
- ConceptFactory.create_remote_concept(self._tx_service,
- iter_res.schemaConcept_subs_iter_res.schemaConcept),
- self._tx_service.run_concept_iter_method(self.id, subs_req))
-
- def sups(self):
- """ Retrieve the all supertypes (direct and higher level) of this schema concept as an iterator """
- sups_req = RequestBuilder.ConceptMethod.SchemaConcept.sups()
- from grakn.service.Session.Concept import ConceptFactory
- return map(lambda iter_res:
- ConceptFactory.create_remote_concept(self._tx_service,
- iter_res.schemaConcept_sups_iter_res.schemaConcept),
- self._tx_service.run_concept_iter_method(self.id, sups_req))
-
-
-class RemoteType(RemoteSchemaConcept):
-
- def is_abstract(self, value=None):
- """
- Get/Set whether this schema Type object is abstract.
- When used as a setter returns `self`
- """
- if value is None:
- # return True/False if the type is set to abstract
- is_abstract_req = RequestBuilder.ConceptMethod.Type.is_abstract()
- method_response = self._tx_service.run_concept_method(self.id, is_abstract_req)
- return method_response.type_isAbstract_res.abstract
- else:
- set_abstract_req = RequestBuilder.ConceptMethod.Type.set_abstract(value)
- method_response = self._tx_service.run_concept_method(self.id, set_abstract_req)
- return self
-
- is_abstract.__annotations__ = {'value': bool, 'return': bool}
-
- def attributes(self):
- """ Retrieve all attributes attached to this Type as an iterator """
- attributes_req = RequestBuilder.ConceptMethod.Type.attributes()
- from grakn.service.Session.Concept import ConceptFactory
- return map(lambda iter_res:
- ConceptFactory.create_remote_concept(self._tx_service,
- iter_res.type_attributes_iter_res.attributeType),
- self._tx_service.run_concept_iter_method(self.id, attributes_req))
-
- def instances(self):
- """ Retrieve all instances of this Type as an iterator """
- instances_req = RequestBuilder.ConceptMethod.Type.instances()
- from grakn.service.Session.Concept import ConceptFactory
- return map(lambda iter_res:
- ConceptFactory.create_remote_concept(self._tx_service,
- iter_res.type_instances_iter_res.thing),
- self._tx_service.run_concept_iter_method(self.id, instances_req))
-
- def playing(self):
- """ Retrieve iterator of roles played by this type """
- playing_req = RequestBuilder.ConceptMethod.Type.playing()
- from grakn.service.Session.Concept import ConceptFactory
- return map(lambda iter_res:
- ConceptFactory.create_remote_concept(self._tx_service,
- iter_res.type_playing_iter_res.role),
- self._tx_service.run_concept_iter_method(self.id, playing_req))
-
- def plays(self, role_concept):
- """ Set a role that is played by this Type """
- plays_req = RequestBuilder.ConceptMethod.Type.plays(role_concept)
- method_response = self._tx_service.run_concept_method(self.id, plays_req)
- return self
-
- def unplay(self, role_concept):
- """ Remove a role that is played by this Type """
- unplay_req = RequestBuilder.ConceptMethod.Type.unplay(role_concept)
- method_response = self._tx_service.run_concept_method(self.id, unplay_req)
- return
-
- def has(self, attribute_concept_type):
- """ Attach an attributeType concept to the type """
- has_req = RequestBuilder.ConceptMethod.Type.has(attribute_concept_type)
- method_response = self._tx_service.run_concept_method(self.id, has_req)
- return self
-
- def unhas(self, attribute_concept_type):
- """ Remove an attribute type concept from this type """
- unhas_req = RequestBuilder.ConceptMethod.Type.unhas(attribute_concept_type)
- method_response = self._tx_service.run_concept_method(self.id, unhas_req)
- return self
-
- def keys(self):
- """ Retrieve an iterator of attribute types that this Type uses as keys """
- keys_req = RequestBuilder.ConceptMethod.Type.keys()
- from grakn.service.Session.Concept import ConceptFactory
- return map(lambda iter_res:
- ConceptFactory.create_remote_concept(self._tx_service,
- iter_res.type_keys_iter_res.attributeType),
- self._tx_service.run_concept_iter_method(self.id, keys_req))
-
- def key(self, attribute_concept_type):
- """ Add an attribute type to be a key for this Type """
- key_req = RequestBuilder.ConceptMethod.Type.key(attribute_concept_type)
- method_response = self._tx_service.run_concept_method(self.id, key_req)
- return self
-
- def unkey(self, attribute_concept_type):
- """ Remove an attribute type from this Type from being a key """
- unkey_req = RequestBuilder.ConceptMethod.Type.unkey(attribute_concept_type)
- method_response = self._tx_service.run_concept_method(self.id, unkey_req)
- return self
-
-
-class RemoteEntityType(RemoteType):
-
- def create(self):
- """ Instantiate an entity of the given type and return it """
- create_req = RequestBuilder.ConceptMethod.EntityType.create()
- method_response = self._tx_service.run_concept_method(self.id, create_req)
- grpc_entity_concept = method_response.entityType_create_res.entity
- from grakn.service.Session.Concept import ConceptFactory
- return ConceptFactory.create_remote_concept(self._tx_service, grpc_entity_concept)
-
-
-class RemoteAttributeType(RemoteType):
-
- def create(self, value):
- """ Create an instance with this AttributeType """
- self_value_type = self.value_type()
- create_inst_req = RequestBuilder.ConceptMethod.AttributeType.create(value, self_value_type)
- method_response = self._tx_service.run_concept_method(self.id, create_inst_req)
- grpc_attribute_concept = method_response.attributeType_create_res.attribute
- from grakn.service.Session.Concept import ConceptFactory
- return ConceptFactory.create_remote_concept(self._tx_service, grpc_attribute_concept)
-
- def attribute(self, value):
- """ Retrieve an attribute instance by value if it exists """
- self_value_type = self.value_type()
- get_attribute_req = RequestBuilder.ConceptMethod.AttributeType.attribute(value, self_value_type)
- method_response = self._tx_service.run_concept_method(self.id, get_attribute_req)
- response = method_response.attributeType_attribute_res
- whichone = response.WhichOneof('res')
- if whichone == 'attribute':
- from grakn.service.Session.Concept import ConceptFactory
- return ConceptFactory.create_remote_concept(self._tx_service, response.attribute)
- elif whichone == 'null':
- return None
- else:
- raise GraknError("Unknown `res` key in AttributeType `attribute` response: {0}".format(whichone))
-
- def value_type(self):
- """ Get the ValueType enum (grakn.ValueType) corresponding to the type of this attribute """
- get_value_type_req = RequestBuilder.ConceptMethod.AttributeType.value_type()
- method_response = self._tx_service.run_concept_method(self.id, get_value_type_req)
- response = method_response.attributeType_valueType_res
- whichone = response.WhichOneof('res')
- if whichone == 'valueType':
- # iterate over enum ValueType enum to find matching data type
- for e in enums.ValueType:
- if e.value == response.valueType:
- return e
- else:
- # loop exited normally
- raise GraknError("Reported valuetype NOT in enum: {0}".format(response.valueType))
- elif whichone == 'null':
- return None
- else:
- raise GraknError("Unknown valuetype response for AttributeType: {0}".format(whichone))
-
- def regex(self, pattern=None):
- """ Get or set regex """
- if pattern is None:
- get_regex_req = RequestBuilder.ConceptMethod.AttributeType.get_regex()
- method_response = self._tx_service.run_concept_method(self.id, get_regex_req)
- return method_response.attributeType_getRegex_res.regex
- else:
- set_regex_req = RequestBuilder.ConceptMethod.AttributeType.set_regex(pattern)
- method_response = self._tx_service.run_concept_method(self.id, set_regex_req)
- return self
-
- regex.__annotations__ = {'pattern': str}
-
-
-class RemoteRelationType(RemoteType):
- def create(self):
- """ Create an instance of a relation with this type """
- create_rel_inst_req = RequestBuilder.ConceptMethod.RelationType.create()
- method_response = self._tx_service.run_concept_method(self.id, create_rel_inst_req)
- grpc_relation_concept = method_response.relationType_create_res.relation
- from grakn.service.Session.Concept import ConceptFactory
- return ConceptFactory.create_remote_concept(self._tx_service, grpc_relation_concept)
-
- def roles(self):
- """ Retrieve roles in this relation schema type """
- get_roles = RequestBuilder.ConceptMethod.RelationType.roles()
- from grakn.service.Session.Concept import ConceptFactory
- return map(lambda iter_res:
- ConceptFactory.create_remote_concept(self._tx_service,
- iter_res.relationType_roles_iter_res.role),
- self._tx_service.run_concept_iter_method(self.id, get_roles))
-
-
- def relates(self, role):
- """ Set a role in this relation schema type """
- relates_req = RequestBuilder.ConceptMethod.RelationType.relates(role)
- method_response = self._tx_service.run_concept_method(self.id, relates_req)
- return self
-
- def unrelate(self, role):
- """ Remove a role in this relation schema type """
- unrelate_req = RequestBuilder.ConceptMethod.RelationType.unrelate(role)
- method_response = self._tx_service.run_concept_method(self.id, unrelate_req)
- return self
-
-
-class RemoteRule(RemoteSchemaConcept):
-
- def get_when(self):
- """ Retrieve the `when` clause for this rule """
- when_req = RequestBuilder.ConceptMethod.Rule.when()
- method_response = self._tx_service.run_concept_method(self.id, when_req)
- response = method_response.rule_when_res
- whichone = response.WhichOneof('res')
- if whichone == 'pattern':
- return response.pattern
- elif whichone == 'null':
- return None
- else:
- raise GraknError("Unknown field in get_when of `rule`: {0}".format(whichone))
-
- def get_then(self):
- """ Retrieve the `then` clause for this rule """
- then_req = RequestBuilder.ConceptMethod.Rule.then()
- method_response = self._tx_service.run_concept_method(self.id, then_req)
- response = method_response.rule_then_res
- whichone = response.WhichOneof('res')
- if whichone == 'pattern':
- return response.pattern
- elif whichone == 'null':
- return None
- else:
- raise GraknError("Unknown field in get_then or `rule`: {0}".format(whichone))
-
-
-class RemoteRole(RemoteSchemaConcept):
-
- def relations(self):
- """ Retrieve relations that this role participates in, as an iterator """
- relations_req = RequestBuilder.ConceptMethod.Role.relations()
- from grakn.service.Session.Concept import ConceptFactory
- return map(lambda iter_res:
- ConceptFactory.create_remote_concept(self._tx_service,
- iter_res.role_relations_iter_res.relationType),
- self._tx_service.run_concept_iter_method(self.id, relations_req))
-
- def players(self):
- """ Retrieve an iterator of entities that play this role """
- players_req = RequestBuilder.ConceptMethod.Role.players()
- from grakn.service.Session.Concept import ConceptFactory
- return map(lambda iter_res:
- ConceptFactory.create_remote_concept(self._tx_service,
- iter_res.role_players_iter_res.type),
- self._tx_service.run_concept_iter_method(self.id, players_req))
-
-
-class RemoteThing(RemoteConcept):
-
- def is_inferred(self):
- """ Is this instance inferred """
- is_inferred_req = RequestBuilder.ConceptMethod.Thing.is_inferred()
- method_response = self._tx_service.run_concept_method(self.id, is_inferred_req)
- return method_response.thing_isInferred_res.inferred
-
- is_inferred.__annotations__ = {'return': bool}
-
- def type(self):
- """ Get the type (schema concept) of this Thing """
- type_req = RequestBuilder.ConceptMethod.Thing.type()
- method_response = self._tx_service.run_concept_method(self.id, type_req)
- from grakn.service.Session.Concept import ConceptFactory
- return ConceptFactory.create_remote_concept(self._tx_service, method_response.thing_type_res.type)
-
- def relations(self, *roles):
- """ Get iterator this Thing's relations, filtered to the optionally provided roles """
- relations_req = RequestBuilder.ConceptMethod.Thing.relations(roles)
- from grakn.service.Session.Concept import ConceptFactory
- return map(lambda iter_res:
- ConceptFactory.create_remote_concept(self._tx_service,
- iter_res.thing_relations_iter_res.relation),
- self._tx_service.run_concept_iter_method(self.id, relations_req))
-
- def attributes(self, *attribute_types):
- """ Retrieve iterator of this Thing's attributes, filtered by optionally provided attribute types """
- attrs_req = RequestBuilder.ConceptMethod.Thing.attributes(attribute_types)
- from grakn.service.Session.Concept import ConceptFactory
- return map(lambda iter_res:
- ConceptFactory.create_remote_concept(self._tx_service,
- iter_res.thing_attributes_iter_res.attribute),
- self._tx_service.run_concept_iter_method(self.id, attrs_req))
-
- def roles(self):
- """ Retrieve iterator of roles this Thing plays """
- roles_req = RequestBuilder.ConceptMethod.Thing.roles()
- from grakn.service.Session.Concept import ConceptFactory
- return map(lambda iter_res:
- ConceptFactory.create_remote_concept(self._tx_service,
- iter_res.thing_roles_iter_res.role),
- self._tx_service.run_concept_iter_method(self.id, roles_req))
-
- def keys(self, *attribute_types):
- """ Retrieve iterator of keys (i.e. actual attributes) of this Thing, filtered by the optionally provided attribute types """
- keys_req = RequestBuilder.ConceptMethod.Thing.keys(attribute_types)
- from grakn.service.Session.Concept import ConceptFactory
- return map(lambda iter_res:
- ConceptFactory.create_remote_concept(self._tx_service,
- iter_res.thing_keys_iter_res.attribute),
- self._tx_service.run_concept_iter_method(self.id, keys_req))
-
- def has(self, attribute):
- """ Attach an attribute instance to this Thing """
- has_req = RequestBuilder.ConceptMethod.Thing.has(attribute)
- method_response = self._tx_service.run_concept_method(self.id, has_req)
- return
-
- def unhas(self, attribute):
- """ Remove an attribute instance from this Thing """
- unhas_req = RequestBuilder.ConceptMethod.Thing.unhas(attribute)
- method_response = self._tx_service.run_concept_method(self.id, unhas_req)
- return
-
-
-class RemoteEntity(RemoteThing):
- pass
-
-
-class RemoteAttribute(RemoteThing):
-
- def value(self):
- """ Retrieve the value contained in this Attribute instance """
- value_req = RequestBuilder.ConceptMethod.Attribute.value()
- method_response = self._tx_service.run_concept_method(self.id, value_req)
- grpc_value_object = method_response.attribute_value_res.value
- from grakn.service.Session.util import ResponseReader
- return ResponseReader.ResponseReader.from_grpc_value_object(grpc_value_object)
-
- def owners(self):
- """ Retrieve entities that have this attribute value """
- owners_req = RequestBuilder.ConceptMethod.Attribute.owners()
- from grakn.service.Session.Concept import ConceptFactory
- return map(lambda iter_res:
- ConceptFactory.create_remote_concept(self._tx_service,
- iter_res.attribute_owners_iter_res.thing),
- self._tx_service.run_concept_iter_method(self.id, owners_req))
-
-
-class RemoteRelation(RemoteThing):
-
- def role_players_map(self):
- """ Retrieve dictionary {role : set(players)} for this relation """
- role_players_map_req = RequestBuilder.ConceptMethod.Relation.role_players_map()
-
- tx_service = self._tx_service
- # create the iterator to obtain all the pairs of (role, player)
- def to_pair(iter_res):
- response = iter_res.relation_rolePlayersMap_iter_res
- from grakn.service.Session.Concept import ConceptFactory
- role = ConceptFactory.create_remote_concept(tx_service, response.role)
- from grakn.service.Session.Concept import ConceptFactory
- player = ConceptFactory.create_remote_concept(tx_service, response.player)
- return (role, player)
-
- # collect all pairs of (role, player) from the iterator (executes over network to Grakn server)
- pairs = list(map(to_pair, self._tx_service.run_concept_iter_method(self.id, role_players_map_req)))
-
- # aggregate into a map from role to set(player)
- # note: need to use role ID as the map key ultimately
- mapping = {}
- id_mapping = {}
- for (role, player) in pairs:
- role_id = role.id
- if role_id in id_mapping:
- role_key = id_mapping[role_id]
- else:
- id_mapping[role_id] = role
- role_key = role
- mapping[role_key] = []
- mapping[role_key].append(player)
-
- return mapping
-
- def role_players(self, *roles):
- """ Retrieve role players filtered by roles """
- role_players_req = RequestBuilder.ConceptMethod.Relation.role_players(roles)
- from grakn.service.Session.Concept import ConceptFactory
- return map(lambda iter_res:
- ConceptFactory.create_remote_concept(self._tx_service,
- iter_res.relation_rolePlayers_iter_res.thing),
- self._tx_service.run_concept_iter_method(self.id, role_players_req))
-
- def assign(self, role, thing):
- """ Assign an entity to a role on this relation instance """
- assign_req = RequestBuilder.ConceptMethod.Relation.assign(role, thing)
- method_response = self._tx_service.run_concept_method(self.id, assign_req)
- return self
-
- def unassign(self, role, thing):
- """ Un-assign an entity from a role on this relation instance """
- unassign_req = RequestBuilder.ConceptMethod.Relation.unassign(role, thing)
- method_response = self._tx_service.run_concept_method(self.id, unassign_req)
- return self
diff --git a/grakn/service/Session/TransactionService.py b/grakn/service/Session/TransactionService.py
deleted file mode 100644
index 3c800e6d..00000000
--- a/grakn/service/Session/TransactionService.py
+++ /dev/null
@@ -1,181 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you 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 six
-from six.moves import map
-
-from grakn.service.Session.util.RequestBuilder import RequestBuilder
-import grakn.service.Session.util.ResponseReader as ResponseReader # for circular import issue
-from grakn.service.Session.util import enums
-from grakn.service.Session.util.Communicator import Communicator
-from grakn.exception.GraknError import GraknError
-
-
-class TransactionService(object):
-
- def __init__(self, session_id, tx_type, transaction_endpoint):
- self.session_id = session_id
- self.tx_type = tx_type.value
-
- self._communicator = Communicator(transaction_endpoint)
-
- # open the transaction with an 'open' message
- open_req = RequestBuilder.open_tx(session_id, tx_type)
- self._communicator.single_request(open_req)
- __init__.__annotations__ = {'tx_type': enums.TxType}
-
- # --- Passthrough targets ---
- # targets of top level Transaction class
-
- def query(self, query, infer, explain, batch_size):
- return Iterator(self._communicator,
- RequestBuilder.start_iterating_query(query, infer, explain, batch_size),
- ResponseReader.ResponseReader.get_query_results(self))
- query.__annotations__ = {'query': str}
-
- def commit(self):
- request = RequestBuilder.commit()
- self._communicator.single_request(request)
-
- def close(self):
- self._communicator.close()
-
- def is_closed(self):
- return self._communicator._closed
-
- def get_concept(self, concept_id):
- request = RequestBuilder.get_concept(concept_id)
- response = self._communicator.single_request(request)
- return ResponseReader.ResponseReader.get_concept(self, response.getConcept_res)
- get_concept.__annotations__ = {'concept_id': str}
-
- def get_schema_concept(self, label):
- request = RequestBuilder.get_schema_concept(label)
- response = self._communicator.single_request(request)
- return ResponseReader.ResponseReader.get_schema_concept(self, response.getSchemaConcept_res)
- get_schema_concept.__annotations__ = {'label': str}
-
- def get_attributes_by_value(self, attribute_value, value_type):
- request = RequestBuilder.start_iterating_get_attributes_by_value(attribute_value, value_type)
- return Iterator(self._communicator, request, ResponseReader.ResponseReader.get_attributes_by_value(self))
- get_attributes_by_value.__annotations__ = {'data_type': enums.ValueType}
-
- def put_entity_type(self, label):
- request = RequestBuilder.put_entity_type(label)
- response = self._communicator.single_request(request)
- return ResponseReader.ResponseReader.put_entity_type(self, response.putEntityType_res)
- put_entity_type.__annotations__ = {'label': str}
-
- def put_relation_type(self, label):
- request = RequestBuilder.put_relation_type(label)
- response = self._communicator.single_request(request)
- return ResponseReader.ResponseReader.put_relation_type(self, response.putRelationType_res)
- put_relation_type.__annotations__ = {'label': str}
-
- def put_attribute_type(self, label, value_type):
- request = RequestBuilder.put_attribute_type(label, value_type)
- response = self._communicator.single_request(request)
- return ResponseReader.ResponseReader.put_attribute_type(self, response.putAttributeType_res)
- put_attribute_type.__annotations__ = {'label': str, 'value_type': enums.ValueType}
-
- def put_role(self, label):
- request = RequestBuilder.put_role(label)
- response = self._communicator.single_request(request)
- return ResponseReader.ResponseReader.put_role(self, response.putRole_res)
- put_role.__annotations__ = {'label': str}
-
- def put_rule(self, label, when, then):
- request = RequestBuilder.put_rule(label, when, then)
- response = self._communicator.single_request(request)
- return ResponseReader.ResponseReader.put_rule(self, response.putRule_res)
- put_rule.__annotations__ = {'label': str, 'when': str, 'then': str}
-
- # --- Transaction Messages ---
-
- def run_concept_method(self, concept_id, grpc_concept_method_req):
- # wrap method_req into a transaction message
- tx_request = RequestBuilder.concept_method_req_to_tx_req(concept_id, grpc_concept_method_req)
- response = self._communicator.single_request(tx_request)
- return response.conceptMethod_res.response
-
- def run_concept_iter_method(self, concept_id, grpc_concept_iter_method_req):
- return Iterator(self._communicator,
- RequestBuilder.start_iterating_concept_method(concept_id, grpc_concept_iter_method_req),
- lambda res: res.conceptMethod_iter_res.response)
-
- def explanation(self, explainable):
- """ Retrieve the explanation of a Concept Map from the server """
- tx_request = RequestBuilder.explanation(explainable)
- response = self._communicator.single_request(tx_request)
- return ResponseReader.ResponseReader.create_explanation(self, response.explanation_res)
-
-
-end_of_batch_results = {'done', 'iteratorId'}
-
-
-def end_of_batch(res):
- res_type = res.iter_res.WhichOneof('res')
- return res_type == 'done' or res_type == 'iteratorId'
-
-
-class Iterator(six.Iterator):
- def __init__(self, communicator, iter_req, resp_converter):
- self._communicator = communicator
- self._iter_req = iter_req
- self._response_iterator = self._communicator.iteration_request(
- RequestBuilder.iter_req_to_tx_req(self._iter_req),
- end_of_batch)
- self._done = False
- self._resp_converter = resp_converter
-
- def __iter__(self):
- return self
-
- def _request_next_batch(self, iter_id):
- self._response_iterator = self._communicator.iteration_request(
- RequestBuilder.iter_req_to_tx_req(
- RequestBuilder.continue_iterating(iter_id, self._iter_req.options)),
- end_of_batch)
-
- def __next__(self):
- if self._done:
- raise GraknError('Iterator was already iterated.')
-
- try:
- response = next(self._response_iterator)
- except StopIteration:
- raise GraknError('Internal client/protocol error,'
- ' did not receive an expected "done" or "iteratorId" message.'
- '\n\n Please ensure client version is supported by server version.')
-
- iter_res = response.iter_res
- res_type = iter_res.WhichOneof('res')
- if res_type == 'done':
- self._done = True
- raise StopIteration
- elif res_type == 'iteratorId':
- self._request_next_batch(iter_res.iteratorId)
- return next(self)
- else:
- return self._resp_converter(iter_res)
-
- def get(self):
- if not self._done:
- self._response_iterator.get()
- return self
diff --git a/grakn/service/Session/util/Communicator.py b/grakn/service/Session/util/Communicator.py
deleted file mode 100644
index 0893eaed..00000000
--- a/grakn/service/Session/util/Communicator.py
+++ /dev/null
@@ -1,219 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you 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.
-#
-
-# Communicator Architecture
-# =========================
-#
-# This communicator allows us to track the sending of multiple requests and match their responses, which may be streams
-# of multiple results, without blocking the initial request call and without any complex threading. This allows the
-# usage of a single stream from a single thread to have predictable results, as there will never be race conditions
-# between requests.
-#
-# The result is that requests are:
-# * Async: like promises.
-# * Serial: always made in the order they are requested by the caller thread.
-#
-# When a request is made, a Resolver is pushed onto the `resolver_queue`. The Resolver is used to *pull* the correct
-# responses from the GRPC stream, which must arrive in a corresponding order but could be pulled by the client in any
-# order. There is no "push" from GRPC, the GRPC stream is pulled until the correct responses are found.
-#
-# No matter which resolver is being resolved, the resolvers *must* be iterated in the order of the `resolver_queue`,
-# since that wil be the order of the responses. If another resolver is ahead of the one we want to retrieve responses
-# for, any responses we find are pushed to its buffer, so that when the user later retrieves responses through that
-# resolver, it will not miss any. **This push only happens when trying to retrieve results out of the order you request
-# them**, it does not happen in a separate thread!
-#
-# As a final step, closing the transaction must ensure that at least one valid response is received for each resolver,
-# to catch and raise any errors before returning control to the user code.
-
-
-import six
-from collections import deque
-from six.moves import queue
-from grakn.exception.GraknError import GraknError
-
-
-class SingleResolver:
- def __init__(self, communicator, request):
- self._request = request
- self._communicator = communicator
- self._buffered_response = None
- communicator._send_with_resolver(self, request)
-
- def _is_last_response(self, response):
- return True
-
- def _buffer_response(self, response):
- self._buffered_response = response
-
- def get(self):
- response = self._buffered_response
- if response:
- return response
- else:
- response = self._communicator._block_for_next(self)
- self._buffered_response = response
- return response
-
- def _end(self):
- pass
-
- def _on_close(self):
- # Ensure any error is correctly raised by waiting for the response even if we are closing
- self.get()
-
-
-class IterationResolver(six.Iterator):
- def __init__(self, communicator, request, is_last_response):
- self._request = request
- self._is_last_response = is_last_response
- self._communicator = communicator
- self._ended = False
- self._started = False
- self._response_buffer = deque()
- communicator._send_with_resolver(self, request)
-
- def __iter__(self):
- return self
-
- def __next__(self):
- # Regardless of the outcome, we must have a result or error before this method returns
- self._started = True
- try:
- # We should first return any results that another resolver has buffered for us
- return self._response_buffer.popleft()
- except IndexError:
- if self._ended:
- raise StopIteration()
- # Block on the GRPC stream and returns the next result, not requiring the buffer
- # This may buffer results for other resolvers
- return self._communicator._block_for_next(self)
-
- def _buffer_response(self, response):
- # Only called when another resolver is pushing a result into our buffer, this may happen before first __next__
- self._started = True
- self._response_buffer.append(response)
-
- def _end(self):
- self._ended = True
-
- def _on_close(self):
- try:
- while not self._ended:
- response = self._communicator._block_for_next(self)
- self._buffer_response(response)
- except StopIteration:
- pass
-
- def get(self):
- if self._started:
- return self
- try:
- self._response_buffer.append(next(self))
- except StopIteration:
- pass
-
-
-class Communicator(six.Iterator):
- def __init__(self, grpc_stream_constructor):
- self._request_queue = queue.Queue()
- self._resolver_queue = deque()
- self._response_iterator = grpc_stream_constructor(self)
- self._closed = False
- self._error = None
-
- def __iter__(self):
- return self
-
- # Used by the GRPC stream to iterate requests as they arrive
- def __next__(self):
- request = self._request_queue.get(block=True)
- if request is None:
- raise StopIteration()
- return request
-
- def error_if_closed(self):
- if self._closed:
- raise GraknError("This connection is closed")
-
- # Put a request for GRPC to consume
- def _send_with_resolver(self, resolver, request):
- self._resolver_queue.append(resolver)
- self._request_queue.put(request)
-
- def _block_for_next(self, resolver):
- self.error_if_closed()
- while True:
- current = None
- try:
- current = self._resolver_queue[0]
- response = next(self._response_iterator)
-
- if current._is_last_response(response):
- self._resolver_queue.popleft()
- current._end()
-
- if current is resolver:
- return response
- else:
- current._buffer_response(response)
-
- except Exception as e:
- if not current:
- self._error = GraknError("Internal client/protocol error, request/response pair not matched: {0}\n\n "
- "Ensure client version is compatible with server version.".format(e))
- else:
- self._error = GraknError("Server/network error: {0}\n\n generated from request: {1}".format(e, current._request))
- self.close()
- raise self._error
-
- def iteration_request(self, request, is_last_response):
- self.error_if_closed()
- return IterationResolver(self, request, is_last_response)
-
- def single_request(self, request):
- self.error_if_closed()
- return SingleResolver(self, request).get()
-
- def close(self):
- if not self._closed:
- raise_error = False
- if not self._error:
- # Exhaust all resolvers, ensuring that transaction is closed without error
- try:
- for resolver in list(self._resolver_queue):
- resolver._on_close()
- except GraknError as e:
- self._error = e
- raise_error = True
- self._closed = True
-
- with self._request_queue.mutex: # probably don't even need the mutex
- self._request_queue.queue.clear()
- self._request_queue.put(None)
-
- # force exhaust the iterator so `onCompleted()` is called on the server
- if not self._error:
- try:
- next(self._response_iterator)
- except StopIteration:
- pass
-
- if raise_error:
- raise self._error
diff --git a/grakn/service/Session/util/RequestBuilder.py b/grakn/service/Session/util/RequestBuilder.py
deleted file mode 100644
index 16899357..00000000
--- a/grakn/service/Session/util/RequestBuilder.py
+++ /dev/null
@@ -1,695 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you 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 datetime import datetime
-
-import grakn_protocol.session.Session_pb2 as transaction_messages
-import grakn_protocol.session.Concept_pb2 as concept_messages
-import grakn_protocol.session.Answer_pb2 as answer_messages
-from grakn.service.Session.util import enums
-from grakn.service.Session.Concept import BaseTypeMapping
-from grakn.exception import GraknError
-
-class QueryOptions(object):
- SERVER_DEFAULT = None
- BATCH_ALL = "all"
-
-class RequestBuilder(object):
- """ Static methods for generating GRPC requests """
-
- @staticmethod
- def _base_iterate_with_options(batch_size):
- iter_options = transaction_messages.Transaction.Iter.Req.Options()
- if batch_size == QueryOptions.BATCH_ALL:
- iter_options.all = True
- elif type(batch_size) == int and batch_size > 0:
- iter_options.number = batch_size
- elif batch_size != QueryOptions.SERVER_DEFAULT:
- raise GraknError("batch_size parameter must either be an integer, SERVER_DEFAULT, or BATCH_ALL")
-
- transaction_iter_req = transaction_messages.Transaction.Iter.Req()
- transaction_iter_req.options.CopyFrom(iter_options)
- return transaction_iter_req
-
- @staticmethod
- def iter_req_to_tx_req(grpc_iter_req):
- transaction_req = transaction_messages.Transaction.Req()
- transaction_req.iter_req.CopyFrom(grpc_iter_req)
- return transaction_req
-
- @staticmethod
- def _query_options(infer, explain):
- options_message = transaction_messages.Transaction.Query.Options()
- if infer != QueryOptions.SERVER_DEFAULT:
- if type(infer) == bool:
- options_message.inferFlag = infer
- else:
- raise GraknError("query 'infer' flag must be SERVER_DEFAULT or a boolean")
- if explain != QueryOptions.SERVER_DEFAULT:
- if type(explain) == bool:
- options_message.explainFlag = explain
- else:
- raise GraknError("query 'explain' flag must be SERVER_DEFAULT or a boolean")
- return options_message
-
- @staticmethod
- def start_iterating_query(query, infer, explain, batch_size):
- query_message = transaction_messages.Transaction.Query.Iter.Req()
- query_message.query = query
- query_options = RequestBuilder._query_options(infer, explain)
- query_message.options.CopyFrom(query_options)
- transaction_iter_req = RequestBuilder._base_iterate_with_options(batch_size)
- transaction_iter_req.query_iter_req.CopyFrom(query_message)
- return transaction_iter_req
-
- @staticmethod
- def start_iterating_concept_method(concept_id, grpc_concept_method_iter_req, batch_size=None):
- transaction_concept_method_iter_req = transaction_messages.Transaction.ConceptMethod.Iter.Req()
- transaction_concept_method_iter_req.id = concept_id
- transaction_concept_method_iter_req.method.CopyFrom(grpc_concept_method_iter_req)
-
- transaction_iter_req = RequestBuilder._base_iterate_with_options(batch_size)
- transaction_iter_req.conceptMethod_iter_req.CopyFrom(transaction_concept_method_iter_req)
- return transaction_iter_req
-
- def start_iterating_get_attributes_by_value(value, valuetype, batch_size=None):
- get_attrs_req = transaction_messages.Transaction.GetAttributes.Iter.Req()
- grpc_value_object = RequestBuilder.ConceptMethod.as_value_object(value, valuetype)
- get_attrs_req.value.CopyFrom(grpc_value_object)
-
- transaction_iter_req = RequestBuilder._base_iterate_with_options(batch_size)
- transaction_iter_req.getAttributes_iter_req.CopyFrom(get_attrs_req)
- return transaction_iter_req
- start_iterating_get_attributes_by_value.__annotations__ = {'valuetype': enums.ValueType}
- start_iterating_get_attributes_by_value = staticmethod(start_iterating_get_attributes_by_value)
-
- @staticmethod
- def continue_iterating(iterator_id, batch_options):
- transaction_iter_req = transaction_messages.Transaction.Iter.Req()
- transaction_iter_req.options.CopyFrom(batch_options)
- transaction_iter_req.iteratorId = iterator_id
- return transaction_iter_req
-
- @staticmethod
- def concept_method_req_to_tx_req(concept_id, grpc_concept_method_req):
- concept_method_req = transaction_messages.Transaction.ConceptMethod.Req()
- concept_method_req.id = concept_id
- concept_method_req.method.CopyFrom(grpc_concept_method_req)
-
- transaction_req = transaction_messages.Transaction.Req()
- transaction_req.conceptMethod_req.CopyFrom(concept_method_req)
- return transaction_req
-
- # --- Top level functionality ---
- @staticmethod
- def open_tx(session_id, tx_type):
- open_request = transaction_messages.Transaction.Open.Req()
- open_request.sessionId = session_id
- open_request.type = tx_type.value
-
- transaction_req = transaction_messages.Transaction.Req()
- transaction_req.open_req.CopyFrom(open_request)
- return transaction_req
-
- @staticmethod
- def open_session(keyspace, credentials):
- open_session_request = transaction_messages.Session.Open.Req()
- open_session_request.Keyspace = keyspace
- if credentials is not None:
- open_session_request.username = credentials['username']
- open_session_request.password = credentials['password']
- return open_session_request
-
- @staticmethod
- def close_session(session_id):
- close_session_request = transaction_messages.Session.Close.Req()
- close_session_request.sessionId = session_id
- return close_session_request
-
- @staticmethod
- def commit():
- commit_req = transaction_messages.Transaction.Commit.Req()
- transaction_req = transaction_messages.Transaction.Req()
- transaction_req.commit_req.CopyFrom(commit_req)
- return transaction_req
-
- @staticmethod
- def get_concept(concept_id):
- get_concept_req = transaction_messages.Transaction.GetConcept.Req()
- get_concept_req.id = concept_id
- transaction_req = transaction_messages.Transaction.Req()
- transaction_req.getConcept_req.CopyFrom(get_concept_req)
- return transaction_req
-
- @staticmethod
- def get_schema_concept(label):
- get_schema_concept_req = transaction_messages.Transaction.GetSchemaConcept.Req()
- get_schema_concept_req.label = label
- transaction_req = transaction_messages.Transaction.Req()
- transaction_req.getSchemaConcept_req.CopyFrom(get_schema_concept_req)
- return transaction_req
-
- @staticmethod
- def put_entity_type(label):
- put_entity_type_req = transaction_messages.Transaction.PutEntityType.Req()
- put_entity_type_req.label = label
- transaction_req = transaction_messages.Transaction.Req()
- transaction_req.putEntityType_req.CopyFrom(put_entity_type_req)
- return transaction_req
-
- @staticmethod
- def put_relation_type(label):
- put_relation_type_req = transaction_messages.Transaction.PutRelationType.Req()
- put_relation_type_req.label = label
- transaction_req = transaction_messages.Transaction.Req()
- transaction_req.putRelationType_req.CopyFrom(put_relation_type_req)
- return transaction_req
-
- def put_attribute_type(label, value_type):
- put_attribute_type_req = transaction_messages.Transaction.PutAttributeType.Req()
- put_attribute_type_req.label = label
- put_attribute_type_req.valueType = value_type.value # retrieve enum value
- transaction_req = transaction_messages.Transaction.Req()
- transaction_req.putAttributeType_req.CopyFrom(put_attribute_type_req)
- return transaction_req
- put_attribute_type.__annotations__ = {'value_type': enums.ValueType}
- put_attribute_type = staticmethod(put_attribute_type)
-
- @staticmethod
- def put_role(label):
- put_role_req = transaction_messages.Transaction.PutRole.Req()
- put_role_req.label = label
- transaction_req = transaction_messages.Transaction.Req()
- transaction_req.putRole_req.CopyFrom(put_role_req)
- return transaction_req
-
- @staticmethod
- def put_rule(label, when, then):
- put_rule_req = transaction_messages.Transaction.PutRule.Req()
- put_rule_req.label = label
- put_rule_req.when = when
- put_rule_req.then = then
- transaction_req = transaction_messages.Transaction.Req()
- transaction_req.putRule_req.CopyFrom(put_rule_req)
- return transaction_req
-
- # --- internal requests ---
-
- @staticmethod
- def explanation(explainable):
- concept_map = {}
- for variable, concept in explainable.map().items():
- grpc_concept = RequestBuilder.ConceptMethod._concept_to_grpc_concept(concept)
- concept_map[variable] = grpc_concept
-
- grpc_concept_map = answer_messages.ConceptMap(map=concept_map)
-
- grpc_concept_map.hasExplanation = explainable.has_explanation()
- grpc_concept_map.pattern = explainable.query_pattern()
-
- explanation_req = answer_messages.Explanation.Req()
- explanation_req.explainable.CopyFrom(grpc_concept_map)
-
- transaction_req = transaction_messages.Transaction.Req()
- transaction_req.explanation_req.CopyFrom(explanation_req)
-
- return transaction_req
-
- # ------ Concept Method Requests ------
-
- class ConceptMethod(object):
- """ Construct Concept Method requests """
-
- @staticmethod
- def delete():
- delete_req = concept_messages.Concept.Delete.Req()
- concept_method_req = concept_messages.Method.Req()
- concept_method_req.concept_delete_req.CopyFrom(delete_req)
- return concept_method_req
-
- @staticmethod
- def _concept_to_grpc_concept(concept):
- """ Takes a concept from ConceptHierarcy and converts to GRPC message """
- grpc_concept = concept_messages.Concept()
- grpc_concept.id = concept.id
- base_type_name = concept.base_type
- grpc_base_type = BaseTypeMapping.name_to_grpc_base_type[base_type_name]
- grpc_concept.baseType = grpc_base_type
- return grpc_concept
-
- def as_value_object(data, valuetype):
- msg = concept_messages.ValueObject()
- if valuetype == enums.ValueType.STRING:
- msg.string = data
- elif valuetype == enums.ValueType.BOOLEAN:
- msg.boolean = data
- elif valuetype == enums.ValueType.INTEGER:
- msg.integer = data
- elif valuetype == enums.ValueType.LONG:
- msg.long = data
- elif valuetype == enums.ValueType.FLOAT:
- msg.float = data
- elif valuetype == enums.ValueType.DOUBLE:
- msg.double = data
- elif valuetype == enums.ValueType.DATETIME:
- # convert local datetime into long
- epoch = datetime(1970, 1, 1)
- diff = data - epoch
- epoch_seconds_utc = int(diff.total_seconds())
- epoch_ms_long_utc = int(epoch_seconds_utc*1000)
- msg.datetime = epoch_ms_long_utc
- else:
- # TODO specialize exception
- raise Exception("Unknown attribute valuetype: {}".format(valuetype))
- return msg
- as_value_object.__annotations__ = {'valuetype': enums.ValueType}
- as_value_object = staticmethod(as_value_object)
-
-
-
- class SchemaConcept(object):
- """ Generates SchemaConcept method messages """
-
- @staticmethod
- def get_label():
- get_schema_label_req = concept_messages.SchemaConcept.GetLabel.Req()
- concept_method_req = concept_messages.Method.Req()
- concept_method_req.schemaConcept_getLabel_req.CopyFrom(get_schema_label_req)
- return concept_method_req
-
- @staticmethod
- def set_label(label):
- set_schema_label_req = concept_messages.SchemaConcept.SetLabel.Req()
- set_schema_label_req.label = label
- concept_method_req = concept_messages.Method.Req()
- concept_method_req.schemaConcept_setLabel_req.CopyFrom(set_schema_label_req)
- return concept_method_req
-
-
- @staticmethod
- def get_sup():
- get_sup_req = concept_messages.SchemaConcept.GetSup.Req()
- concept_method_req = concept_messages.Method.Req()
- concept_method_req.schemaConcept_getSup_req.CopyFrom(get_sup_req)
- return concept_method_req
-
- @staticmethod
- def set_sup(concept):
- grpc_concept = RequestBuilder.ConceptMethod._concept_to_grpc_concept(concept)
- set_sup_req = concept_messages.SchemaConcept.SetSup.Req()
- set_sup_req.schemaConcept.CopyFrom(grpc_concept)
- concept_method_req = concept_messages.Method.Req()
- concept_method_req.schemaConcept_setSup_req.CopyFrom(set_sup_req)
- return concept_method_req
-
- @staticmethod
- def subs():
- subs_req = concept_messages.SchemaConcept.Subs.Iter.Req()
- concept_method_req = concept_messages.Method.Iter.Req()
- concept_method_req.schemaConcept_subs_iter_req.CopyFrom(subs_req)
- return concept_method_req
-
- @staticmethod
- def sups():
- sups_req = concept_messages.SchemaConcept.Sups.Iter.Req()
- concept_method_req = concept_messages.Method.Iter.Req()
- concept_method_req.schemaConcept_sups_iter_req.CopyFrom(sups_req)
- return concept_method_req
-
- class Rule(object):
- """ Generates Rule method messages """
-
- @staticmethod
- def when():
- when_req = concept_messages.Rule.When.Req()
- concept_method_req = concept_messages.Method.Req()
- concept_method_req.rule_when_req.CopyFrom(when_req)
- return concept_method_req
-
- @staticmethod
- def then():
- then_req = concept_messages.Rule.Then.Req()
- concept_method_req = concept_messages.Method.Req()
- concept_method_req.rule_then_req.CopyFrom(then_req)
- return concept_method_req
-
- class Role(object):
- """ Generates Role method messages """
-
- @staticmethod
- def relations():
- relations_req = concept_messages.Role.Relations.Iter.Req()
- concept_method_req = concept_messages.Method.Iter.Req()
- concept_method_req.role_relations_iter_req.CopyFrom(relations_req)
- return concept_method_req
-
- @staticmethod
- def players():
- players_req = concept_messages.Role.Players.Iter.Req()
- concept_method_req = concept_messages.Method.Iter.Req()
- concept_method_req.role_players_iter_req.CopyFrom(players_req)
- return concept_method_req
-
- class Type(object):
- """ Generates Type method messages """
-
- @staticmethod
- def is_abstract():
- is_abstract_req = concept_messages.Type.IsAbstract.Req()
- concept_method_req = concept_messages.Method.Req()
- concept_method_req.type_isAbstract_req.CopyFrom(is_abstract_req)
- return concept_method_req
-
- @staticmethod
- def set_abstract(abstract):
- set_abstract_req = concept_messages.Type.SetAbstract.Req()
- assert type(abstract) == bool
- set_abstract_req.abstract = abstract
- concept_method_req = concept_messages.Method.Req()
- concept_method_req.type_setAbstract_req.CopyFrom(set_abstract_req)
- return concept_method_req
-
- @staticmethod
- def instances():
- type_instances_req = concept_messages.Type.Instances.Iter.Req()
- concept_method_req = concept_messages.Method.Iter.Req()
- concept_method_req.type_instances_iter_req.CopyFrom(type_instances_req)
- return concept_method_req
-
- @staticmethod
- def keys():
- type_keys_req = concept_messages.Type.Keys.Iter.Req()
- concept_method_req = concept_messages.Method.Iter.Req()
- concept_method_req.type_keys_iter_req.CopyFrom(type_keys_req)
- return concept_method_req
-
- @staticmethod
- def attributes():
- type_attributes_req = concept_messages.Type.Attributes.Iter.Req()
- concept_method_req = concept_messages.Method.Iter.Req()
- concept_method_req.type_attributes_iter_req.CopyFrom(type_attributes_req)
- return concept_method_req
-
- @staticmethod
- def has(attribute_type_concept):
- grpc_concept = RequestBuilder.ConceptMethod._concept_to_grpc_concept(attribute_type_concept)
- has_req = concept_messages.Type.Has.Req()
- has_req.attributeType.CopyFrom(grpc_concept)
- concept_method_req = concept_messages.Method.Req()
- concept_method_req.type_has_req.CopyFrom(has_req)
- return concept_method_req
-
- @staticmethod
- def unhas(attribute_type_concept):
- grpc_concept = RequestBuilder.ConceptMethod._concept_to_grpc_concept(attribute_type_concept)
- unhas_req = concept_messages.Type.Unhas.Req()
- unhas_req.attributeType.CopyFrom(grpc_concept)
- concept_method_req = concept_messages.Method.Req()
- concept_method_req.type_unhas_req.CopyFrom(unhas_req)
- return concept_method_req
-
- @staticmethod
- def key(attribute_type_concept):
- grpc_concept = RequestBuilder.ConceptMethod._concept_to_grpc_concept(attribute_type_concept)
- key_req = concept_messages.Type.Key.Req()
- key_req.attributeType.CopyFrom(grpc_concept)
- concept_method_req = concept_messages.Method.Req()
- concept_method_req.type_key_req.CopyFrom(key_req)
- return concept_method_req
-
- @staticmethod
- def unkey(attribute_type_concept):
- grpc_concept = RequestBuilder.ConceptMethod._concept_to_grpc_concept(attribute_type_concept)
- unkey_req = concept_messages.Type.Unkey.Req()
- unkey_req.attributeType.CopyFrom(grpc_concept)
- concept_method_req = concept_messages.Method.Req()
- concept_method_req.type_unkey_req.CopyFrom(unkey_req)
- return concept_method_req
-
- @staticmethod
- def playing():
- playing_req = concept_messages.Type.Playing.Iter.Req()
- concept_method_req = concept_messages.Method.Iter.Req()
- concept_method_req.type_playing_iter_req.CopyFrom(playing_req)
- return concept_method_req
-
- @staticmethod
- def plays(role_concept):
- grpc_concept = RequestBuilder.ConceptMethod._concept_to_grpc_concept(role_concept)
- plays_req = concept_messages.Type.Plays.Req()
- plays_req.role.CopyFrom(grpc_concept)
- concept_method_req = concept_messages.Method.Req()
- concept_method_req.type_plays_req.CopyFrom(plays_req)
- return concept_method_req
-
- @staticmethod
- def unplay(role_concept):
- grpc_concept = RequestBuilder.ConceptMethod._concept_to_grpc_concept(role_concept)
- unplay_req = concept_messages.Type.Unplay.Req()
- unplay_req.role.CopyFrom(grpc_concept)
- concept_method_req = concept_messages.Method.Req()
- concept_method_req.type_unplay_req.CopyFrom(unplay_req)
- return concept_method_req
-
- class EntityType(object):
- """ Generates EntityType method messages """
-
- @staticmethod
- def create():
- create_req = concept_messages.EntityType.Create.Req()
- concept_method_req = concept_messages.Method.Req()
- concept_method_req.entityType_create_req.CopyFrom(create_req)
- return concept_method_req
-
- class RelationType(object):
- """ Generates RelationType method messages """
-
- @staticmethod
- def create():
- create_req = concept_messages.RelationType.Create.Req()
- concept_method_req = concept_messages.Method.Req()
- concept_method_req.relationType_create_req.CopyFrom(create_req)
- return concept_method_req
-
- @staticmethod
- def roles():
- roles_req = concept_messages.RelationType.Roles.Iter.Req()
- concept_messages_req = concept_messages.Method.Iter.Req()
- concept_messages_req.relationType_roles_iter_req.CopyFrom(roles_req)
- return concept_messages_req
-
- @staticmethod
- def relates(role_concept):
- grpc_role_concept = RequestBuilder.ConceptMethod._concept_to_grpc_concept(role_concept)
- relates_req = concept_messages.RelationType.Relates.Req()
- relates_req.role.CopyFrom(grpc_role_concept)
- concept_messages_req = concept_messages.Method.Req()
- concept_messages_req.relationType_relates_req.CopyFrom(relates_req)
- return concept_messages_req
-
- @staticmethod
- def unrelate(role_concept):
- grpc_role_concept = RequestBuilder.ConceptMethod._concept_to_grpc_concept(role_concept)
- unrelate_req = concept_messages.RelationType.Unrelate.Req()
- unrelate_req.role.CopyFrom(grpc_role_concept)
- concept_messages_req = concept_messages.Method.Req()
- concept_messages_req.relationType_unrelate_req.CopyFrom(unrelate_req)
- return concept_messages_req
-
- class AttributeType(object):
- """ Generates AttributeType method messages """
-
- @staticmethod
- def create(value, valuetype):
- grpc_value_object = RequestBuilder.ConceptMethod.as_value_object(value, valuetype)
- create_attr_req = concept_messages.AttributeType.Create.Req()
- create_attr_req.value.CopyFrom(grpc_value_object)
- concept_method_req = concept_messages.Method.Req()
- concept_method_req.attributeType_create_req.CopyFrom(create_attr_req)
- return concept_method_req
-
- @staticmethod
- def attribute(value, valuetype):
- grpc_value_object = RequestBuilder.ConceptMethod.as_value_object(value, valuetype)
- attribute_req = concept_messages.AttributeType.Attribute.Req()
- attribute_req.value.CopyFrom(grpc_value_object)
- concept_method_req = concept_messages.Method.Req()
- concept_method_req.attributeType_attribute_req.CopyFrom(attribute_req)
- return concept_method_req
-
- @staticmethod
- def value_type():
- valuetype_req = concept_messages.AttributeType.ValueType.Req()
- concept_method_req = concept_messages.Method.Req()
- concept_method_req.attributeType_valueType_req.CopyFrom(valuetype_req)
- return concept_method_req
-
- @staticmethod
- def get_regex():
- get_regex_req = concept_messages.AttributeType.GetRegex.Req()
- concept_method_req = concept_messages.Method.Req()
- concept_method_req.attributeType_getRegex_req.CopyFrom(get_regex_req)
- return concept_method_req
-
- @staticmethod
- def set_regex(regex):
- set_regex_req = concept_messages.AttributeType.SetRegex.Req()
- set_regex_req.regex = regex
- concept_method_req = concept_messages.Method.Req()
- concept_method_req.attributeType_setRegex_req.CopyFrom(set_regex_req)
- return concept_method_req
-
- class Thing(object):
- """ Generates Thing method messages """
-
- @staticmethod
- def is_inferred():
- is_inferred_req = concept_messages.Thing.IsInferred.Req()
- concept_method_req = concept_messages.Method.Req()
- concept_method_req.thing_isInferred_req.CopyFrom(is_inferred_req)
- return concept_method_req
-
- @staticmethod
- def type():
- type_req = concept_messages.Thing.Type.Req()
- concept_method_req = concept_messages.Method.Req()
- concept_method_req.thing_type_req.CopyFrom(type_req)
- return concept_method_req
-
- @staticmethod
- def attributes(attribute_types=[]):
- """ Takes a list of AttributeType concepts to narrow attribute retrieval """
- attributes_req = concept_messages.Thing.Attributes.Iter.Req()
- for attribute_type_concept in attribute_types:
- grpc_attr_type_concept = RequestBuilder.ConceptMethod._concept_to_grpc_concept(attribute_type_concept)
- attributes_req.attributeTypes.extend([grpc_attr_type_concept])
- concept_method_req = concept_messages.Method.Iter.Req()
- concept_method_req.thing_attributes_iter_req.CopyFrom(attributes_req)
- return concept_method_req
-
- @staticmethod
- def relations(role_concepts=[]):
- """ Takes a list of role concepts to narrow the relations retrieval """
- relations_req = concept_messages.Thing.Relations.Iter.Req()
- for role_concept in role_concepts:
- grpc_role_concept = RequestBuilder.ConceptMethod._concept_to_grpc_concept(role_concept)
- # TODO this could use .add() if can be made to work...
- relations_req.roles.extend([grpc_role_concept])
- concept_method_req = concept_messages.Method.Iter.Req()
- concept_method_req.thing_relations_iter_req.CopyFrom(relations_req)
- return concept_method_req
-
- @staticmethod
- def roles():
- roles_req = concept_messages.Thing.Roles.Iter.Req()
- concept_method_req = concept_messages.Method.Iter.Req()
- concept_method_req.thing_roles_iter_req.CopyFrom(roles_req)
- return concept_method_req
-
- @staticmethod
- def keys(attribute_types=[]):
- """ Takes a list of AttributeType concepts to narrow the key retrieval """
- keys_req = concept_messages.Thing.Keys.Iter.Req()
- for attribute_type_concept in attribute_types:
- grpc_attr_type_concept = RequestBuilder.ConceptMethod._concept_to_grpc_concept(attribute_type_concept)
- keys_req.attributeTypes.extend([grpc_attr_type_concept])
- concept_method_req = concept_messages.Method.Iter.Req()
- concept_method_req.thing_keys_iter_req.CopyFrom(keys_req)
- return concept_method_req
-
- @staticmethod
- def has(attribute_concept):
- grpc_attribute_concept = RequestBuilder.ConceptMethod._concept_to_grpc_concept(attribute_concept)
- has_req = concept_messages.Thing.Has.Req()
- has_req.attribute.CopyFrom(grpc_attribute_concept)
- concept_method_req = concept_messages.Method.Req()
- concept_method_req.thing_has_req.CopyFrom(has_req)
- return concept_method_req
-
- @staticmethod
- def unhas(attribute_concept):
- grpc_attribute_concept = RequestBuilder.ConceptMethod._concept_to_grpc_concept(attribute_concept)
- unhas_req = concept_messages.Thing.Unhas.Req()
- unhas_req.attribute.CopyFrom(grpc_attribute_concept)
- concept_method_req = concept_messages.Method.Req()
- concept_method_req.thing_unhas_req.CopyFrom(unhas_req)
- return concept_method_req
-
- class Relation(object):
- """ Generates Relation method messages """
-
- @staticmethod
- def role_players_map():
- role_players_map_req = concept_messages.Relation.RolePlayersMap.Iter.Req()
- concept_method_req = concept_messages.Method.Iter.Req()
- concept_method_req.relation_rolePlayersMap_iter_req.CopyFrom(role_players_map_req)
- return concept_method_req
-
- @staticmethod
- def role_players(roles=[]):
- """ Retrieve concepts that can play the given roles """
- role_players_req = concept_messages.Relation.RolePlayers.Iter.Req()
- for role_concept in roles:
- grpc_role_concept = RequestBuilder.ConceptMethod._concept_to_grpc_concept(role_concept)
- role_players_req.roles.extend([grpc_role_concept])
- concept_method_req = concept_messages.Method.Iter.Req()
- concept_method_req.relation_rolePlayers_iter_req.CopyFrom(role_players_req)
- return concept_method_req
-
- @staticmethod
- def assign(role_concept, player_concept):
- grpc_role_concept = RequestBuilder.ConceptMethod._concept_to_grpc_concept(role_concept)
- grpc_player_concept = RequestBuilder.ConceptMethod._concept_to_grpc_concept(player_concept)
- assign_req = concept_messages.Relation.Assign.Req()
- assign_req.role.CopyFrom(grpc_role_concept)
- assign_req.player.CopyFrom(grpc_player_concept)
- concept_method_req = concept_messages.Method.Req()
- concept_method_req.relation_assign_req.CopyFrom(assign_req)
- return concept_method_req
-
- @staticmethod
- def unassign(role_concept, player_concept):
- grpc_role_concept = RequestBuilder.ConceptMethod._concept_to_grpc_concept(role_concept)
- grpc_player_concept = RequestBuilder.ConceptMethod._concept_to_grpc_concept(player_concept)
- unassign_req = concept_messages.Relation.Unassign.Req()
- unassign_req.role.CopyFrom(grpc_role_concept)
- unassign_req.player.CopyFrom(grpc_player_concept)
- concept_method_req = concept_messages.Method.Req()
- concept_method_req.relation_unassign_req.CopyFrom(unassign_req)
- return concept_method_req
-
- class Attribute(object):
- """ Generates Attribute method messages """
-
- @staticmethod
- def value():
- value_req = concept_messages.Attribute.Value.Req()
- concept_method_req = concept_messages.Method.Req()
- concept_method_req.attribute_value_req.CopyFrom(value_req)
- return concept_method_req
-
- @staticmethod
- def owners():
- owners_req = concept_messages.Attribute.Owners.Iter.Req()
- concept_method_req = concept_messages.Method.Iter.Req()
- concept_method_req.attribute_owners_iter_req.CopyFrom(owners_req)
- return concept_method_req
-
- class Entity(object):
- """ Empty implementation -- never create requests on Entity """
- pass
diff --git a/grakn/service/Session/util/ResponseReader.py b/grakn/service/Session/util/ResponseReader.py
deleted file mode 100644
index 84c5c583..00000000
--- a/grakn/service/Session/util/ResponseReader.py
+++ /dev/null
@@ -1,346 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you 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 datetime
-from grakn.service.Session.util import enums
-from grakn.service.Session.Concept import ConceptFactory
-from grakn.exception.GraknError import GraknError
-from six.moves import map
-
-
-class ResponseReader(object):
-
- @staticmethod
- def get_query_results(tx_service):
- return lambda iterate_res: AnswerConverter.convert(tx_service, iterate_res.query_iter_res.answer)
-
- @staticmethod
- def get_concept(tx_service, grpc_get_schema_concept):
- which_one = grpc_get_schema_concept.WhichOneof("res")
- if which_one == "concept":
- grpc_concept = grpc_get_schema_concept.concept
- return ConceptFactory.create_remote_concept(tx_service, grpc_concept)
- elif which_one == "null":
- return None
- else:
- raise GraknError("Unknown get_concept response: {0}".format(which_one))
-
- @staticmethod
- def get_schema_concept(tx_service, grpc_get_concept):
- which_one = grpc_get_concept.WhichOneof("res")
- if which_one == "schemaConcept":
- grpc_concept = grpc_get_concept.schemaConcept
- return ConceptFactory.create_remote_concept(tx_service, grpc_concept)
- elif which_one == "null":
- return None
- else:
- raise GraknError("Unknown get_schema_concept response: {0}".format(which_one))
-
- @staticmethod
- def get_attributes_by_value(tx_service):
- return lambda iterate_res: ConceptFactory.create_remote_concept(tx_service, iterate_res.getAttributes_iter_res.attribute)
-
- @staticmethod
- def put_entity_type(tx_service, grpc_put_entity_type):
- return ConceptFactory.create_remote_concept(tx_service, grpc_put_entity_type.entityType)
-
- @staticmethod
- def put_relation_type(tx_service, grpc_put_relation_type):
- return ConceptFactory.create_remote_concept(tx_service, grpc_put_relation_type.relationType)
-
- @staticmethod
- def put_attribute_type(tx_service, grpc_put_attribute_type):
- return ConceptFactory.create_remote_concept(tx_service, grpc_put_attribute_type.attributeType)
-
- @staticmethod
- def put_role(tx_service, grpc_put_role):
- return ConceptFactory.create_remote_concept(tx_service, grpc_put_role.role)
-
- @staticmethod
- def put_rule(tx_service, grpc_put_rule):
- return ConceptFactory.create_remote_concept(tx_service, grpc_put_rule.rule)
-
- @staticmethod
- def from_grpc_value_object(grpc_value_object):
- whichone = grpc_value_object.WhichOneof('value')
- # check the one is in the known ValueTypes
- known_valuetypes = [e.name.lower() for e in enums.ValueType]
- if whichone.lower() not in known_valuetypes:
- raise GraknError("Unknown value object value key: {0}, not in {1}".format(whichone, known_valuetypes))
- if whichone == 'string':
- return grpc_value_object.string
- elif whichone == 'boolean':
- return grpc_value_object.boolean
- elif whichone == 'integer':
- return grpc_value_object.integer
- elif whichone == 'long':
- return grpc_value_object.long
- elif whichone == 'float':
- return grpc_value_object.float
- elif whichone == 'double':
- return grpc_value_object.double
- elif whichone == 'datetime':
- epoch_ms_utc = grpc_value_object.datetime
- local_datetime_utc = datetime.datetime.fromtimestamp(float(epoch_ms_utc)/1000.)
- return local_datetime_utc
- else:
- raise GraknError("Unknown valuetype in enum but not handled in from_grpc_value_object")
-
- @staticmethod
- def from_grpc_value_type_res(grpc_value_type_res):
- whichone = grpc_value_type_res.WhichOneof('res')
- if whichone == 'valueType':
- # iterate over enum ValueType enum to find matching data type
- for e in enums.ValueType:
- if e.value == grpc_value_type_res.valueType:
- return e
- else:
- # loop exited normally
- raise GraknError("Reported valuetype NOT in enum: {0}".format(grpc_value_type_res.valueType))
- elif whichone == 'null':
- return None
- else:
- raise GraknError("Unknown valuetype response for AttributeType: {0}".format(whichone))
-
- # --- concept method helpers ---
-
- @staticmethod
- def create_explanation(tx_service, grpc_explanation_res):
- """ Convert gRPC explanation response to explanation object """
- grpc_list_of_concept_maps = grpc_explanation_res.explanation
- native_list_of_concept_maps = []
- for grpc_concept_map in grpc_list_of_concept_maps:
- native_list_of_concept_maps.append(AnswerConverter._create_concept_map(tx_service, grpc_concept_map))
- rule = ConceptFactory.create_local_concept(grpc_explanation_res.rule)
- if len(rule.id) == 0:
- rule = None
- return Explanation(native_list_of_concept_maps, rule)
-
-
-class Explanation(object):
-
- def __init__(self, list_of_concept_maps, rule):
- self._concept_maps_list = list_of_concept_maps
- self._rule = rule
-
- def get_answers(self):
- """ Return answers this explanation is dependent on"""
- # note that concept_maps are subtypes of Answer
- return self._concept_maps_list
-
- def get_rule(self):
- """ return the rule that is being explained, if there is one """
- return self._rule
-
-
-# ----- Different types of answers -----
-
-class AnswerGroup(object):
-
- def __init__(self, owner_concept, answer_list):
- self._owner_concept = owner_concept
- self._answer_list = answer_list
-
- def get(self):
- return self
-
- def owner(self):
- return self._owner_concept
-
- def answers(self):
- return self._answer_list
-
-
-class ConceptMap(object):
-
- def __init__(self, concept_map, query_pattern, has_explanation, tx_service):
- self._concept_map = concept_map
- self._has_explanation = has_explanation
- self._query_pattern = query_pattern
- self._tx_service = tx_service
-
- def get(self, var=None):
- """ Get the indicated variable's Concept from the map or this ConceptMap """
- if var is None:
- return self
- else:
- if var not in self._concept_map:
- # TODO specialize exception
- raise GraknError("Variable {0} is not in the ConceptMap".format(var))
- return self._concept_map[var]
-
- def query_pattern(self):
- return self._query_pattern
-
- def has_explanation(self):
- return self._has_explanation
-
- def explanation(self):
- if self._has_explanation:
- return self._tx_service.explanation(self)
- else:
- raise GraknError("Explanation not available on concept map: " + str(self))
-
- def map(self):
- """ Get the map from Variable (str) to Concept objects """
- return self._concept_map
-
- def vars(self):
- """ Get a set of vars in the map """
- return set(self._concept_map.keys())
-
- def contains_var(self, var):
- """ Check whether the map contains the var """
- return var in self._concept_map
-
- def is_empty(self):
- """ Check if the variable map is empty """
- return len(self._concept_map) == 0
-
-
-class ConceptList(object):
-
- def __init__(self, concept_id_list):
- self._concept_id_list = concept_id_list
-
- def list(self):
- """ Get the list of concept IDs """
- return self._concept_id_list
-
-
-class ConceptSet(object):
-
- def __init__(self, concept_id_set):
- self._concept_id_set = concept_id_set
- __init__.__annotations__ = {'_concept_id_set': 'List[str]'}
-
- def set(self):
- """ Return the set of Concept IDs within this ConceptSet """
- return self._concept_id_set
-
-
-class ConceptSetMeasure(ConceptSet):
-
- def __init__(self, concept_id_set, number):
- super(ConceptSetMeasure, self).__init__(concept_id_set)
- self._measurement = number
- __init__.__annotations__ = {'_measurement': float}
-
- def measurement(self):
- return self._measurement
-
-
-class Value(object):
-
- def __init__(self, number):
- self._number = number
- __init__.__annotations__ = {'number': float}
-
- def number(self):
- """ Get as number (float or int) """
- return self._number
-
-
-class Void(object):
- def __init__(self, message):
- self._message = message
- __init__.__annotations__ = {'message': str}
-
- def message(self):
- """ Get the message on this Void answer type """
- return self._message
-
-
-class AnswerConverter(object):
- """ Static methods to convert answers into Answer objects """
-
- @staticmethod
- def convert(tx_service, grpc_answer):
- which_one = grpc_answer.WhichOneof('answer')
-
- if which_one == 'conceptMap':
- return AnswerConverter._create_concept_map(tx_service, grpc_answer.conceptMap)
- elif which_one == 'answerGroup':
- return AnswerConverter._create_answer_group(tx_service, grpc_answer.answerGroup)
- elif which_one == 'conceptList':
- return AnswerConverter._create_concept_list(tx_service, grpc_answer.conceptList)
- elif which_one == 'conceptSet':
- return AnswerConverter._create_concept_set(tx_service, grpc_answer.conceptSet)
- elif which_one == 'conceptSetMeasure':
- return AnswerConverter._create_concept_set_measure(tx_service, grpc_answer.conceptSetMeasure)
- elif which_one == 'value':
- return AnswerConverter._create_value(tx_service, grpc_answer.value)
- elif which_one == 'void':
- return AnswerConverter._create_void(tx_service, grpc_answer.void)
- else:
- raise GraknError('Unknown gRPC Answer.answer message type: {0}'.format(which_one))
-
- @staticmethod
- def _create_concept_map(tx_service, grpc_concept_map_msg):
- """ Create a Concept Dictionary from the grpc response """
- var_concept_map = grpc_concept_map_msg.map
- answer_map = {}
- for (variable, grpc_concept) in var_concept_map.items():
- answer_map[variable] = ConceptFactory.create_local_concept(grpc_concept)
-
- query_pattern = grpc_concept_map_msg.pattern
- has_explanation = grpc_concept_map_msg.hasExplanation
-
- return ConceptMap(answer_map, query_pattern, has_explanation, tx_service)
-
- @staticmethod
- def _create_answer_group(tx_service, grpc_answer_group):
- grpc_owner_concept = grpc_answer_group.owner
- owner_concept = ConceptFactory.create_local_concept(grpc_owner_concept)
- grpc_answers = list(grpc_answer_group.answers)
- answer_list = [AnswerConverter.convert(tx_service, grpc_answer) for grpc_answer in grpc_answers]
- return AnswerGroup(owner_concept, answer_list)
-
- @staticmethod
- def _create_concept_list(tx_service, grpc_concept_list_msg):
- ids_list = list(grpc_concept_list_msg.list.ids)
- return ConceptList(ids_list)
-
- @staticmethod
- def _create_concept_set(tx_service, grpc_concept_set_msg):
- ids_set = set(grpc_concept_set_msg.set.ids)
- return ConceptSet(ids_set)
-
- @staticmethod
- def _create_concept_set_measure(tx_service, grpc_concept_set_measure):
- concept_ids = list(grpc_concept_set_measure.set.ids)
- number = grpc_concept_set_measure.measurement.value
- return ConceptSetMeasure(concept_ids, AnswerConverter._number_string_to_native(number))
-
- @staticmethod
- def _create_value(tx_service, grpc_value_msg):
- number = grpc_value_msg.number.value
- return Value(AnswerConverter._number_string_to_native(number))
-
- @staticmethod
- def _create_void(tx_service, grpc_void):
- """ Convert grpc Void message into an object """
- return Void(grpc_void.message)
-
- @staticmethod
- def _number_string_to_native(number):
- try:
- return int(number)
- except ValueError:
- return float(number)
diff --git a/grakn/service/Session/util/enums.py b/grakn/service/Session/util/enums.py
deleted file mode 100644
index d52aea72..00000000
--- a/grakn/service/Session/util/enums.py
+++ /dev/null
@@ -1,40 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you 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 enum import Enum
-import grakn_protocol.session.Session_pb2 as SessionMessages
-import grakn_protocol.session.Concept_pb2 as ConceptMessages
-
-
-class TxType(Enum):
- READ = SessionMessages.Transaction.Type.Value('READ')
- WRITE = SessionMessages.Transaction.Type.Value('WRITE')
- BATCH = SessionMessages.Transaction.Type.Value('BATCH')
-
-
-VALUE_TYPE_map = {}
-
-class ValueType(Enum):
- STRING = ConceptMessages.AttributeType.VALUE_TYPE.Value('STRING')
- BOOLEAN = ConceptMessages.AttributeType.VALUE_TYPE.Value('BOOLEAN')
- INTEGER = ConceptMessages.AttributeType.VALUE_TYPE.Value('INTEGER')
- LONG = ConceptMessages.AttributeType.VALUE_TYPE.Value('LONG')
- FLOAT = ConceptMessages.AttributeType.VALUE_TYPE.Value('FLOAT')
- DOUBLE = ConceptMessages.AttributeType.VALUE_TYPE.Value('DOUBLE')
- DATETIME = ConceptMessages.AttributeType.VALUE_TYPE.Value('DATETIME')
diff --git a/requirements.txt b/requirements.txt
index 9849d529..c3fcc8f3 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,3 +1,24 @@
-grpcio==1.24.1,<2
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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.
+#
+
+--extra-index-url https://repo.grakn.ai/repository/pypi-snapshot/simple
+graknprotocol==0.0.0-6bf8c601ecd57a2869cde56c17eec8784a9a2804
+grpcio==1.33.2
protobuf==3.6.1
six>=1.11.0
diff --git a/test/BUILD b/test/BUILD
new file mode 100644
index 00000000..709acfda
--- /dev/null
+++ b/test/BUILD
@@ -0,0 +1,92 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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.
+#
+
+load("@graknlabs_common//test/server:rules.bzl", "native_grakn_artifact")
+load("@graknlabs_dependencies//tool/checkstyle:rules.bzl", "checkstyle_test")
+load("@rules_python//python:defs.bzl", "py_library", "py_test")
+
+native_grakn_artifact(
+ name = "native-grakn-artifact",
+ mac_artifact = "@graknlabs_grakn_core_artifact_mac//file",
+ linux_artifact = "@graknlabs_grakn_core_artifact_linux//file",
+ windows_artifact = "@graknlabs_grakn_core_artifact_windows//file",
+ visibility = ["//test:__subpackages__"],
+)
+
+py_test(
+ name = "test_concept",
+ srcs = [
+ "integration/base.py",
+ "integration/test_concept.py"
+ ],
+ deps = [
+ "//:client_python",
+ ],
+ data = [":native-grakn-artifact"],
+ args = ["$(location :native-grakn-artifact)"],
+ python_version = "PY3"
+)
+
+py_test(
+ name = "test_connection",
+ srcs = [
+ "integration/base.py",
+ "integration/test_connection.py"
+ ],
+ deps = [
+ "//:client_python",
+ ],
+ data = [":native-grakn-artifact"],
+ args = ["$(location :native-grakn-artifact)"],
+ python_version = "PY3"
+)
+
+py_test(
+ name = "test_query",
+ srcs = [
+ "integration/base.py",
+ "integration/test_query.py"
+ ],
+ deps = [
+ "//:client_python",
+ ],
+ size = "large",
+ data = [":native-grakn-artifact"],
+ args = ["$(location :native-grakn-artifact)"],
+ python_version = "PY3"
+)
+
+test_suite(
+ name = "test_integration",
+ tests = [
+ ":test_concept",
+ ":test_connection",
+ ":test_query",
+ ]
+)
+
+checkstyle_test(
+ name = "checkstyle",
+ include = glob([
+ "*",
+ "deployment/*",
+ "integration/*",
+ ]),
+ license_type = "apache",
+)
diff --git a/test/deployment/requirements.txt b/test/deployment/requirements.txt
new file mode 100644
index 00000000..267cebca
--- /dev/null
+++ b/test/deployment/requirements.txt
@@ -0,0 +1,20 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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.
+#
+
+https://repo.grakn.ai/repository/pypi-snapshot-group/packages/grakn-client/CLIENT_PYTHON_VERSION_MARKER/grakn-client-CLIENT_PYTHON_VERSION_MARKER.tar.gz
\ No newline at end of file
diff --git a/tests/deployment/test.py b/test/deployment/test.py
similarity index 63%
rename from tests/deployment/test.py
rename to test/deployment/test.py
index eb30a0c3..f2b0312c 100644
--- a/tests/deployment/test.py
+++ b/test/deployment/test.py
@@ -1,8 +1,26 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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 unittest import TestCase
from grakn.client import GraknClient
-
class PythonApplicationTest(TestCase):
""" Very basic tests to ensure no error occur when performing simple operations with the test grakn-client distribution"""
diff --git a/tests/integration/base.py b/test/integration/base.py
similarity index 70%
rename from tests/integration/base.py
rename to test/integration/base.py
index d34245a4..cf3e412a 100755
--- a/tests/integration/base.py
+++ b/test/integration/base.py
@@ -28,6 +28,7 @@
import tarfile
+# TODO: update to work with Grakn 2.0
class GraknServer(object):
DISTRIBUTION_LOCATION = sys.argv.pop()
@@ -35,18 +36,18 @@ def __init__(self):
self.__distribution_root_dir = None
self.__unpacked_dir = None
- def __enter__(self):
- if not self.__unpacked_dir:
- self._unpack()
- sp.check_call([
- 'grakn', 'server', 'start'
- ], cwd=os.path.join(self.__unpacked_dir, self.__distribution_root_dir))
+ def __enter__(self): pass
+ # if not self.__unpacked_dir:
+ # self._unpack()
+ # sp.check_call([
+ # 'grakn', 'server'
+ # ], cwd=os.path.join(self.__unpacked_dir, self.__distribution_root_dir))
- def __exit__(self, exc_type, exc_val, exc_tb):
- sp.check_call([
- 'grakn', 'server', 'stop'
- ], cwd=os.path.join(self.__unpacked_dir, self.__distribution_root_dir))
- shutil.rmtree(self.__unpacked_dir)
+ def __exit__(self, exc_type, exc_val, exc_tb): pass
+ # sp.check_call([
+ # 'grakn', 'server', 'stop'
+ # ], cwd=os.path.join(self.__unpacked_dir, self.__distribution_root_dir))
+ # shutil.rmtree(self.__unpacked_dir)
def _unpack(self):
self.__unpacked_dir = tempfile.mkdtemp(prefix='grakn')
@@ -55,13 +56,13 @@ def _unpack(self):
self.__distribution_root_dir = os.path.commonpath(tf.getnames()[1:])
-class test_Base(TestCase):
+class test_base(TestCase):
""" Sets up DB for use in tests """
@classmethod
def setUpClass(cls):
- super(test_Base, cls).setUpClass()
+ super(test_base, cls).setUpClass()
@classmethod
def tearDownClass(cls):
- super(test_Base, cls).tearDownClass()
+ super(test_base, cls).tearDownClass()
diff --git a/test/integration/test_concept.py b/test/integration/test_concept.py
new file mode 100644
index 00000000..51bda174
--- /dev/null
+++ b/test/integration/test_concept.py
@@ -0,0 +1,287 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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 unittest
+from datetime import datetime
+
+from grakn.client import GraknClient, SessionType, TransactionType, ValueType
+from grakn.common.exception import GraknClientException
+from test.integration.base import test_base, GraknServer
+
+
+class TestConcept(test_base):
+ @classmethod
+ def setUpClass(cls):
+ super(TestConcept, cls).setUpClass()
+ global client
+ client = GraknClient()
+
+ @classmethod
+ def tearDownClass(cls):
+ super(TestConcept, cls).tearDownClass()
+ client.close()
+
+ def setUp(self):
+ if "grakn" in client.databases().all():
+ client.databases().delete("grakn")
+ client.databases().create("grakn")
+
+ def test_get_supertypes(self):
+ with client.session("grakn", SessionType.SCHEMA) as session:
+ with session.transaction(TransactionType.WRITE) as tx:
+ lion = tx.concepts().put_entity_type("lion")
+ for lion_supertype in lion.as_remote(tx).get_supertypes():
+ print(str(lion_supertype) + " is a supertype of 'lion'")
+
+ def test_streaming_operation_on_closed_tx(self):
+ with client.session("grakn", SessionType.SCHEMA) as session:
+ with session.transaction(TransactionType.WRITE) as tx:
+ lion = tx.concepts().put_entity_type("lion")
+ tx.close()
+ try:
+ for _ in lion.as_remote(tx).get_supertypes():
+ self.fail()
+ self.fail()
+ except GraknClientException:
+ pass
+
+ def test_invalid_streaming_operation(self):
+ with client.session("grakn", SessionType.SCHEMA) as session:
+ with session.transaction(TransactionType.WRITE) as tx:
+ lion = tx.concepts().put_entity_type("lion")
+ lion._label = "lizard"
+ try:
+ for _ in lion.as_remote(tx).get_supertypes():
+ self.fail()
+ self.fail()
+ except GraknClientException:
+ pass
+
+ def test_get_many_instances(self):
+ with client.session("grakn", SessionType.SCHEMA) as session:
+ with session.transaction(TransactionType.WRITE) as tx:
+ goldfish_type = tx.concepts().put_entity_type("goldfish")
+ tx.commit()
+ with client.session("grakn", SessionType.DATA) as session:
+ with session.transaction(TransactionType.WRITE) as tx:
+ for _ in range(100):
+ goldfish_type.as_remote(tx).create()
+ goldfish_count = sum(1 for _ in goldfish_type.as_remote(tx).get_instances())
+ print("There are " + str(goldfish_count) + " goldfish.")
+
+ def test_stone_lions(self):
+ # SCHEMA OPERATIONS
+ with client.session("grakn", SessionType.SCHEMA) as session:
+ with session.transaction(TransactionType.WRITE) as tx:
+ lion = tx.concepts().put_entity_type("lion")
+ tx.commit()
+ print("put_entity_type - SUCCESS")
+
+ with session.transaction(TransactionType.WRITE) as tx:
+ lionFamily = tx.concepts().put_relation_type("lion-family")
+ lionFamily.as_remote(tx).set_relates("lion-cub")
+ lionCub = next(lionFamily.as_remote(tx).get_relates())
+ lion.as_remote(tx).set_plays(lionCub)
+ tx.commit()
+ print("put_relation_type / set_relates / set_plays - SUCCESS")
+
+ with session.transaction(TransactionType.WRITE) as tx:
+ maneSize = tx.concepts().put_attribute_type("mane-size", ValueType.LONG)
+ lion.as_remote(tx).set_owns(maneSize)
+ tx.commit()
+ print("commit attribute type + owns - SUCCESS")
+
+ with session.transaction(TransactionType.WRITE) as tx:
+ stoneLion = tx.concepts().put_entity_type("stone-lion")
+ stoneLion.as_remote(tx).set_supertype(lion)
+ tx.commit()
+ print("set supertype - SUCCESS")
+
+ with session.transaction(TransactionType.READ) as tx:
+ supertypeOfLion = lion.as_remote(tx).get_supertype()
+ tx.close()
+ print("get supertype - SUCCESS - the supertype of 'lion' is '" + supertypeOfLion.get_label() + "'")
+
+ with session.transaction(TransactionType.READ) as tx:
+ supertypesOfStoneLion = list(map(lambda x: x.get_label(), stoneLion.as_remote(tx).get_supertypes()))
+ print("get supertypes - SUCCESS - the supertypes of 'stone-lion' are " + str(supertypesOfStoneLion))
+
+ with session.transaction(TransactionType.READ) as tx:
+ subtypesOfLion = list(map(lambda x: x.get_label(), lion.as_remote(tx).get_subtypes()))
+ print("get subtypes - SUCCESS - the subtypes of 'lion' are " + str(subtypesOfLion))
+
+ with session.transaction(TransactionType.WRITE) as tx:
+ monkey = tx.concepts().put_entity_type("monkey")
+ monkey.as_remote(tx).set_label("orangutan")
+ newLabel = tx.concepts().get_entity_type("orangutan").get_label()
+ tx.rollback()
+ assert newLabel == "orangutan"
+ print("set label - SUCCESS - 'monkey' has been renamed to '" + newLabel + "'.")
+
+ with session.transaction(TransactionType.WRITE) as tx:
+ whale = tx.concepts().put_entity_type("whale")
+ whale.as_remote(tx).set_abstract()
+ isAbstractAfterSet = whale.as_remote(tx).is_abstract()
+ assert isAbstractAfterSet
+ print("set abstract - SUCCESS - 'whale' " + ("is" if isAbstractAfterSet else "is not") + " abstract.")
+ whale.as_remote(tx).unset_abstract()
+ isAbstractAfterUnset = whale.as_remote(tx).is_abstract()
+ assert not isAbstractAfterUnset
+ tx.rollback()
+ print("unset abstract - SUCCESS - 'whale' " + ("is still" if isAbstractAfterUnset else "is no longer") + " abstract.")
+
+ with session.transaction(TransactionType.WRITE) as tx:
+ parentship = tx.concepts().put_relation_type("parentship")
+ parentship.as_remote(tx).set_relates("parent")
+ fathership = tx.concepts().put_relation_type("fathership")
+ fathership.as_remote(tx).set_supertype(parentship)
+ fathership.as_remote(tx).set_relates("father", "parent")
+ person = tx.concepts().put_entity_type("person")
+ parent = parentship.as_remote(tx).get_relates("parent")
+ person.as_remote(tx).set_plays(parent)
+ man = tx.concepts().put_entity_type("man")
+ man.as_remote(tx).set_supertype(person)
+ father = fathership.as_remote(tx).get_relates("father")
+ man.as_remote(tx).set_plays(father, parent)
+ playingRoles = list(map(lambda role: role.get_scoped_label(), man.as_remote(tx).get_plays()))
+ roleplayers = list(map(lambda player: player.get_label(), father.as_remote(tx).get_players()))
+ tx.commit()
+ assert "fathership:father" in playingRoles
+ assert "man" in roleplayers
+ print("get/set relates/plays/players, overriding a super-role - SUCCESS - 'man' plays " + str(playingRoles) + "; 'fathership:father' is played by " + str(roleplayers))
+
+ with session.transaction(TransactionType.WRITE) as tx:
+ email = tx.concepts().put_attribute_type("email", ValueType.STRING)
+ email.as_remote(tx).set_abstract()
+ workEmail = tx.concepts().put_attribute_type("work-email", ValueType.STRING)
+ workEmail.as_remote(tx).set_supertype(email)
+ age = tx.concepts().put_attribute_type("age", ValueType.LONG)
+ assert age.is_long()
+ person.as_remote(tx).set_abstract()
+ person.as_remote(tx).set_owns(attribute_type=email, is_key=True)
+ person.as_remote(tx).set_owns(attribute_type=age, is_key=False)
+ lion.as_remote(tx).set_owns(attribute_type=age)
+ customer = tx.concepts().put_entity_type("customer")
+ customer.as_remote(tx).set_supertype(person)
+ customer.as_remote(tx).set_owns(attribute_type=workEmail, is_key=True, overridden_type=email)
+ ownedAttributes = list(map(lambda x: x.get_label(), customer.as_remote(tx).get_owns()))
+ ownedKeys = list(map(lambda x: x.get_label(), customer.as_remote(tx).get_owns(keys_only=True)))
+ ownedDateTimes = list(map(lambda x: x.get_label(), customer.as_remote(tx).get_owns(ValueType.DATETIME, keys_only=False)))
+ tx.commit()
+ assert len(ownedAttributes) == 2
+ assert len(ownedKeys) == 1
+ assert len(ownedDateTimes) == 0
+ print("get/set owns, overriding a super-attribute - SUCCESS - 'customer' owns " + str(ownedAttributes) + ", "
+ "of which " + str(ownedKeys) + " are keys, and " + str(ownedDateTimes) + " are datetimes")
+
+ with session.transaction(TransactionType.WRITE) as tx:
+ person.as_remote(tx).unset_owns(age)
+ person.as_remote(tx).unset_plays(parent)
+ fathership.as_remote(tx).unset_relates("father")
+ personOwns = list(map(lambda x: x.get_label(), person.as_remote(tx).get_owns()))
+ personPlays = list(map(lambda x: x.get_label(), person.as_remote(tx).get_plays()))
+ fathershipRelates = list(map(lambda x: x.get_label(), fathership.as_remote(tx).get_relates()))
+ tx.rollback()
+ assert "age" not in personOwns
+ assert "parent" not in personPlays
+ assert "father" not in fathershipRelates
+ print("unset owns/plays/relates - SUCCESS - 'person' owns " + str(personOwns) + ", "
+ "'person' plays " + str(personPlays) + ", 'fathership' relates " + str(fathershipRelates))
+
+ with session.transaction(TransactionType.WRITE) as tx:
+ password = tx.concepts().put_attribute_type("password", ValueType.STRING)
+ shoeSize = tx.concepts().put_attribute_type("shoe-size", ValueType.LONG)
+ volume = tx.concepts().put_attribute_type("volume", ValueType.DOUBLE)
+ isAlive = tx.concepts().put_attribute_type("is-alive", ValueType.BOOLEAN)
+ startDate = tx.concepts().put_attribute_type("start-date", ValueType.DATETIME)
+ tx.commit()
+ print("put all 5 attribute value types - SUCCESS - password is a " + password.get_value_type().name + ", shoe-size is a " + shoeSize.get_value_type().name + ", "
+ "volume is a " + volume.get_value_type().name + ", is-alive is a " + isAlive.get_value_type().name + " and start-date is a " + startDate.get_value_type().name)
+
+ with session.transaction(TransactionType.WRITE) as tx:
+ tx.logic().put_rule("septuagenarian-rule", "{$x isa person;}", "$x has age 70")
+ tx.commit()
+ print("put rule - SUCCESS")
+
+ # DATA OPERATIONS
+ with client.session("grakn", SessionType.DATA) as session:
+ with session.transaction(TransactionType.WRITE) as tx:
+ for _ in range(10): stoneLion.as_remote(tx).create()
+ lions = list(lion.as_remote(tx).get_instances())
+ firstLion = lions[0]
+ isInferred = firstLion.as_remote(tx).is_inferred()
+ lionType = firstLion.as_remote(tx).get_type()
+ age42 = age.as_remote(tx).put(42)
+ firstLion.as_remote(tx).set_has(age42)
+ firstLionAttrs = list(map(lambda x: x.get_value(), firstLion.as_remote(tx).get_has()))
+ assert len(firstLionAttrs) == 1
+ assert firstLionAttrs[0] == 42
+ firstLionAges = list(map(lambda x: x.get_value(), firstLion.as_remote(tx).get_has(age)))
+ assert len(firstLionAges) == 1
+ assert firstLionAges[0] == 42
+ firstLionWorkEmails = list(map(lambda x: x.get_value(), firstLion.as_remote(tx).get_has(workEmail)))
+ assert len(firstLionWorkEmails) == 0
+ firstFamily = lionFamily.as_remote(tx).create()
+ firstFamily.as_remote(tx).add_player(lionCub, firstLion)
+ firstLionPlaying = list(map(lambda x: x.get_scoped_label(), firstLion.as_remote(tx).get_plays()))
+ assert len(firstLionPlaying) == 1
+ assert firstLionPlaying[0] == "lion-family:lion-cub"
+ firstLionRelations = list(firstLion.as_remote(tx).get_relations())
+ assert len(firstLionRelations) == 1
+ firstLionFatherRelations = list(firstLion.as_remote(tx).get_relations([father]))
+ assert len(firstLionFatherRelations) == 0
+ tx.commit()
+ assert len(lions) == 10
+ assert not isInferred
+ print("Thing methods - SUCCESS - There are " + str(len(lions)) + " lions.")
+ assert lionType.get_label() == "stone-lion"
+ print("getType - SUCCESS - After looking more closely, it turns out that there are " + str(len(lions)) + " stone lions.")
+
+ with session.transaction(TransactionType.WRITE) as tx:
+ firstLionFamily = next(lionFamily.as_remote(tx).get_instances())
+ firstLion = next(firstLionFamily.as_remote(tx).get_players())
+ firstLionFamily2 = next(firstLion.as_remote(tx).get_relations())
+ assert firstLionFamily2
+ players = list(firstLionFamily.as_remote(tx).get_players())
+ assert len(players) == 1
+ lionCubPlayers = list(firstLionFamily.as_remote(tx).get_players([lionCub]))
+ assert len(lionCubPlayers) == 1
+ playersByRoleType = firstLionFamily.as_remote(tx).get_players_by_role_type().items()
+ (firstRole, firstPlayer) = next(iter(playersByRoleType))
+ assert firstRole.get_scoped_label() == "lion-family:lion-cub"
+ firstLionFamily.as_remote(tx).remove_player(lionCub, firstLion)
+ lionFamilyCleanedUp = firstLionFamily.as_remote(tx).is_deleted()
+ assert(lionFamilyCleanedUp)
+ tx.rollback()
+ print("Relation methods - SUCCESS")
+
+ with session.transaction(TransactionType.WRITE) as tx:
+ passwordAttr = password.as_remote(tx).put("rosebud")
+ shoeSizeAttr = shoeSize.as_remote(tx).put(9)
+ volumeAttr = volume.as_remote(tx).put(1.618)
+ isAliveAttr = isAlive.as_remote(tx).put(bool("hopefully"))
+ startDateAttr = startDate.as_remote(tx).put(datetime.now())
+ tx.commit()
+ print("put 5 different types of attributes - SUCCESS - password is " + passwordAttr.get_value() + ", shoe-size is " + str(shoeSizeAttr.get_value()) + ", "
+ "volume is " + str(volumeAttr.get_value()) + ", is-alive is " + str(isAliveAttr.get_value()) + " and start-date is " + str(startDateAttr.get_value()))
+
+
+if __name__ == "__main__":
+ with GraknServer():
+ unittest.main(verbosity=2)
diff --git a/test/integration/test_connection.py b/test/integration/test_connection.py
new file mode 100644
index 00000000..3770fdf6
--- /dev/null
+++ b/test/integration/test_connection.py
@@ -0,0 +1,61 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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 unittest
+from grakn.client import GraknClient, SessionType, TransactionType
+from test.integration.base import test_base, GraknServer
+
+
+class TestConnection(test_base):
+ @classmethod
+ def setUpClass(cls):
+ super(TestConnection, cls).setUpClass()
+ global client
+ client = GraknClient()
+
+ @classmethod
+ def tearDownClass(cls):
+ super(TestConnection, cls).tearDownClass()
+ global client
+ client.close()
+
+ def test_database(self):
+ dbs = client.databases().all()
+ if "grakn" in dbs:
+ client.databases().delete("grakn")
+ client.databases().create("grakn")
+ dbs = client.databases().all()
+ self.assertTrue("grakn" in dbs)
+
+ def test_session(self):
+ if "grakn" not in client.databases().all():
+ client.databases().create("grakn")
+ session = client.session("grakn", SessionType.SCHEMA)
+ session.close()
+
+ def test_transaction(self):
+ if "grakn" not in client.databases().all():
+ client.databases().create("grakn")
+ with client.session("grakn", SessionType.SCHEMA) as session:
+ with session.transaction(TransactionType.WRITE) as tx:
+ pass
+
+if __name__ == "__main__":
+ with GraknServer():
+ unittest.main(verbosity=2)
diff --git a/tests/integration/test_grakn.py b/test/integration/test_grakn.py
similarity index 96%
rename from tests/integration/test_grakn.py
rename to test/integration/test_grakn.py
index fb8461d1..75283359 100644
--- a/tests/integration/test_grakn.py
+++ b/test/integration/test_grakn.py
@@ -20,14 +20,14 @@
import unittest
import uuid
import grakn
-from grakn.client import GraknClient, ValueType, Transaction
-from grakn.exception.GraknError import GraknError
-from grakn.service.Session.util.ResponseReader import Value
+from grakn import GraknClient, ValueType, Transaction
+from grakn import GraknError
-from tests.integration.base import test_Base, GraknServer
+from test.integration.base import test_base, GraknServer
-class test_client_PreDbSetup(test_Base):
+# TODO: we should ensure that all these tests are migrated to BDD
+class test_client_PreDbSetup(test_base):
""" Tests Database interactions *before* anything needs to be inserted/created """
# --- Test grakn client instantiation for one URI ---
@@ -66,14 +66,14 @@ def test_client_session_valid_keyspace(self):
""" Test OK uri and keyspace """
a_inst = GraknClient('localhost:48555')
a_session = a_inst.session('test')
- self.assertIsInstance(a_session, grakn.client.Session)
+ self.assertIsInstance(a_session, grakn.rpc.Session)
tx = a_session.transaction().read()
tx.close()
a_session.close()
# test the `with` statement
with a_inst.session('test') as session:
- self.assertIsInstance(session, grakn.client.Session)
+ self.assertIsInstance(session, grakn.rpc.Session)
tx = session.transaction().read()
tx.close()
@@ -103,7 +103,7 @@ def test_client_tx_valid_enum(self):
client = GraknClient('localhost:48555')
a_session = client.session('test')
tx = a_session.transaction().read()
- self.assertIsInstance(tx, grakn.client.Transaction)
+ self.assertIsInstance(tx, grakn.rpc.Transaction)
client.close()
def test_client_tx_invalid_enum(self):
@@ -117,13 +117,13 @@ def test_client_tx_invalid_enum(self):
client = None
session = None
-class test_client_Base(test_Base):
+class test_client_base(test_base):
""" Sets up DB for use in tests """
@classmethod
def setUpClass(cls):
""" Make sure we have some sort of schema and data in DB, only done once """
- super(test_client_Base, cls).setUpClass()
+ super(test_client_base, cls).setUpClass()
global client, session
client = GraknClient("localhost:48555")
@@ -153,7 +153,7 @@ def setUpClass(cls):
@classmethod
def tearDownClass(cls):
- super(test_client_Base, cls).tearDownClass()
+ super(test_client_base, cls).tearDownClass()
global client, session
session.close()
@@ -171,7 +171,7 @@ def cleanupTransaction(self, tx):
-class test_Transaction(test_client_Base):
+class test_Transaction(test_client_base):
""" Class for testing transaction methods, eg query, put attribute type... """
# --- query tests ---
diff --git a/test/integration/test_query.py b/test/integration/test_query.py
new file mode 100644
index 00000000..6971d286
--- /dev/null
+++ b/test/integration/test_query.py
@@ -0,0 +1,61 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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 unittest
+from grakn.client import GraknClient, SessionType, TransactionType
+from test.integration.base import test_base, GraknServer
+
+
+class TestQuery(test_base):
+ @classmethod
+ def setUpClass(cls):
+ global client
+ client = GraknClient()
+
+ @classmethod
+ def tearDownClass(cls):
+ client.close()
+
+ def setUp(self):
+ if "grakn" not in client.databases().all():
+ client.databases().create("grakn")
+
+ def test_define_and_undef_relation_type(self):
+ with client.session("grakn", SessionType.SCHEMA) as session:
+ with session.transaction(TransactionType.WRITE) as tx:
+ tx.query().define("define lionfight sub relation, relates victor, relates loser;")
+ lionfight_type = tx.concepts().get_type("lionfight")
+ print(lionfight_type._label)
+ tx.query().undefine("undefine lionfight sub relation;")
+ tx.commit()
+
+ def test_insert_some_entities(self):
+ with client.session("grakn", SessionType.SCHEMA) as session:
+ with session.transaction(TransactionType.WRITE) as tx:
+ tx.query().define("define lion sub entity;")
+ tx.commit()
+ with client.session("grakn", SessionType.DATA) as session:
+ with session.transaction(TransactionType.WRITE) as tx:
+ for answer in tx.query().insert("insert $a isa lion; $b isa lion; $c isa lion;"):
+ print(answer)
+
+
+if __name__ == "__main__":
+ with GraknServer():
+ unittest.main(verbosity=2)
diff --git a/tests/deployment/requirements.txt b/tests/deployment/requirements.txt
deleted file mode 100644
index 9eee3011..00000000
--- a/tests/deployment/requirements.txt
+++ /dev/null
@@ -1 +0,0 @@
-https://repo.grakn.ai/repository/pypi-snapshot-group/packages/grakn-client/CLIENT_PYTHON_VERSION_MARKER/grakn-client-CLIENT_PYTHON_VERSION_MARKER.tar.gz
\ No newline at end of file
diff --git a/tests/integration/test_answer.py b/tests/integration/test_answer.py
deleted file mode 100644
index 8472e932..00000000
--- a/tests/integration/test_answer.py
+++ /dev/null
@@ -1,296 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you 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 unittest
-from grakn.client import GraknClient
-from grakn.service.Session.util.ResponseReader import Value, ConceptList, ConceptSet, ConceptSetMeasure, AnswerGroup, \
- Void
-from tests.integration.base import test_Base, GraknServer
-
-client = None
-
-
-class test_Answers(test_Base):
- @classmethod
- def setUpClass(cls):
- global client
- client = GraknClient("localhost:48555")
-
- @classmethod
- def tearDownClass(cls):
- client.close()
-
- @staticmethod
- def _build_parentship(tx):
- """ Helper to set up some state to test answers in a tx/keyspace """
- parentship_type = tx.put_relation_type("parentship")
- parentship = parentship_type.create()
- parent_role = tx.put_role("parent")
- child_role = tx.put_role("child")
- parentship_type.relates(parent_role)
- parentship_type.relates(child_role)
- person_type = tx.put_entity_type("person")
- person_type.plays(parent_role)
- person_type.plays(child_role)
- parent = person_type.create()
- child = person_type.create()
- parentship.assign(child_role, child)
- parentship.assign(parent_role, parent)
- tx.commit() # closes the tx
- return {'child': child.id, 'parent': parent.id, 'parentship': parentship.id}
-
-
- def test_shortest_path_answer_ConceptList(self):
- """ Test shortest path which returns a ConceptList """
- with client.session("shortestpath") as local_session:
- tx = local_session.transaction().write()
- parentship_map = test_Answers._build_parentship(tx) # this closes the tx
- tx = local_session.transaction().write()
- result = tx.query('compute path from {0}, to {1};'.format(parentship_map['parent'], parentship_map['child']))
- answer = next(result)
- self.assertIsInstance(answer, ConceptList)
- self.assertEqual(len(answer.list()), 3)
- self.assertTrue(parentship_map['parent'] in answer.list())
- self.assertTrue(parentship_map['child'] in answer.list())
- self.assertTrue(parentship_map['parentship'] in answer.list())
- tx.close()
- client.keyspaces().delete("shortestpath")
-
- def test_cluster_anwer_ConceptSet(self):
- """ Test clustering with connected components response as ConceptSet """
- with client.session("clusterkeyspace") as local_session:
- tx = local_session.transaction().write()
- parentship_map = test_Answers._build_parentship(tx) # this closes the tx
- tx = local_session.transaction().write()
- result = tx.query("compute cluster in [person, parentship], using connected-component;")
- concept_set_answer = next(result)
- self.assertIsInstance(concept_set_answer, ConceptSet)
- self.assertEqual(len(concept_set_answer.set()), 3)
- self.assertTrue(parentship_map['parent'] in concept_set_answer.set())
- self.assertTrue(parentship_map['child'] in concept_set_answer.set())
- self.assertTrue(parentship_map['parentship'] in concept_set_answer.set())
- tx.close()
- client.keyspaces().delete("clusterkeyspace")
-
-
- def test_compute_centrality_answer_ConceptSetMeasure(self):
- """ Test compute centrality, response type ConceptSetMeasure """
- with client.session("centralitykeyspace") as local_session:
- tx = local_session.transaction().write()
- parentship_map = test_Answers._build_parentship(tx) # this closes the tx
- tx = local_session.transaction().write()
- result = tx.query("compute centrality in [person, parentship], using degree;")
- concept_set_measure_answer = next(result)
- self.assertIsInstance(concept_set_measure_answer, ConceptSetMeasure)
- self.assertEqual(concept_set_measure_answer.measurement(), 1)
- self.assertTrue(parentship_map['parent'] in concept_set_measure_answer.set())
- self.assertTrue(parentship_map['child'] in concept_set_measure_answer.set())
- tx.close()
- client.keyspaces().delete("centralitykeyspace")
-
-
- def test_compute_aggregate_group_answer_AnswerGroup(self):
- """ Test compute aggreate count, response type AnwerGroup """
- with client.session("aggregategroup") as local_session:
- tx = local_session.transaction().write()
- parentship_map = test_Answers._build_parentship(tx) # this closes the tx
- tx = local_session.transaction().write()
- result = tx.query("match $x isa person; $y isa person; (parent: $x, child: $y) isa parentship; get; group $x;")
- answer_group = next(result)
- self.assertIsInstance(answer_group, AnswerGroup)
- self.assertEqual(answer_group.owner().id, parentship_map['parent'])
- self.assertEqual(answer_group.answers()[0].get('x').id, parentship_map['parent'])
- self.assertEqual(answer_group.answers()[0].map()['y'].id, parentship_map['child'])
- tx.close()
- client.keyspaces().delete("aggregategroup")
-
-
- def test_delete_returns_Void(self):
- """ Test `match...delete`, response type should be Void"""
- with client.session("matchdelete_void") as local_session:
- tx = local_session.transaction().write()
- tx.query("define person sub entity;")
- result = list(tx.query("insert $x isa person;"))
- inserted_person = result[0].get("x")
- person_id = inserted_person.id
-
- void_result = list(tx.query("match $x id {0}; delete $x isa thing;".format(person_id)))[0]
- self.assertIsInstance(void_result, Void)
- self.assertTrue("Deleted" in void_result.message())
-
- self.assertTrue(inserted_person.as_remote(tx).is_deleted())
- tx.close()
- client.keyspaces().delete("matchdelete_void")
-
- def test_compute_count_empty_graph_answer_Value(self):
- with client.session("countingzero") as local_session:
- tx = local_session.transaction().write()
- tx.put_entity_type("foo")
- result = tx.query("compute count in foo;")
- answer = next(result)
- self.assertIsInstance(answer, Value)
- self.assertEqual(answer.number(), 0)
- tx.close()
- client.keyspaces().delete("countingzero")
-
- def test_aggr_count_empty_graph_answer_Value(self):
- with client.session("countingnonzero") as local_session:
- tx = local_session.transaction().write()
- tx.query("define person sub entity; dog sub entity;")
- result = tx.query("match $x sub entity; get $x; count;")
- answer = next(result)
- self.assertIsInstance(answer, Value)
- self.assertEqual(answer.number(), 3)
- tx.close()
- client.keyspaces().delete("countingnonzero")
-
- @unittest.skip("behaviour changed on server side")
- def test_conceptmap_explanation(self):
- """ Test explanations when hitting a transitive rule """
- with client.session("transitivity") as local_session:
- tx = local_session.transaction().write()
- tx.query("""
- define
- object sub entity, plays owned, plays owner;
- ownership sub relation, relates owned, relates owner;
- transitive-ownership sub rule, when {
- (owned: $x, owner: $y) isa ownership;
- (owned: $y, owner: $z) isa ownership;
- }, then {
- (owned: $x, owner: $z) isa ownership;
- };
- """)
- tx.query("""
- insert
- $a isa object; $b isa object; $c isa object; $d isa object; $e isa object;
- (owned: $a, owner: $b) isa ownership;
- (owned: $b, owner: $c) isa ownership;
- (owned: $c, owner: $d) isa ownership;
- (owned: $d, owner: $e) isa ownership;
- """)
- tx.commit()
-
- tx = local_session.transaction().write()
- answers = tx.query("match (owner: $x, owned: $y) isa ownership; get;")
-
- has_explanation = 0
- no_explanation = 0
- for concept_map in answers:
- pattern = concept_map.query_pattern()
- if concept_map.has_explanation():
- explanation = concept_map.explanation()
- self.assertIsNotNone(explanation)
- self.assertTrue(len(pattern) > 0)
- for var in concept_map.map():
- self.assertTrue(("$" + var) in pattern)
- has_explanation += 1
- else:
- self.assertTrue(len(pattern) == 0)
- no_explanation += 1
-
- tx.close()
- self.assertEqual(no_explanation, 4)
- self.assertEqual(has_explanation, 6)
- client.keyspaces().delete("transitivity")
-
-
- def test_get_explanation_has_rule(self):
- """ Test that explanations have rules attached """
- with client.session("explanation_has_rule") as local_session:
- tx = local_session.transaction().write()
- tx.query("define family-name sub attribute, value string;"
- "parenthood sub relation, relates parent, relates child;"
- "person sub entity, has family-name, plays parent, plays child;"
- "family-name-inheritence sub rule,"
- "when { (parent: $p, child: $c) isa parenthood; $p has family-name $f; },"
- "then { $c has family-name $f; };")
- tx.query("insert $bob isa person, has family-name \"bobson\";"
- "$bobjr isa person;"
- "(parent: $bob, child: $bobjr) isa parenthood;")
-
- tx.commit()
- tx = local_session.transaction().read()
-
- answers = tx.query("match $x isa person, has family-name $f; get;", explain=True)
- for x in answers:
- if x.has_explanation():
- explanation = x.explanation()
- self.assertIsNotNone(explanation.get_rule())
-
- client.keyspaces().delete("explanation_has_rule")
-
-
- def test_query_with_explain_false_no_explanations_available(self):
- with client.session("query_explain_false") as local_session:
- tx = local_session.transaction().write()
- tx.query("define family-name sub attribute, value string;"
- "parenthood sub relation, relates parent, relates child;"
- "person sub entity, has family-name, plays parent, plays child;"
- "family-name-inheritence sub rule,"
- "when { (parent: $p, child: $c) isa parenthood; $p has family-name $f; },"
- "then { $c has family-name $f; };")
- tx.query("insert $bob isa person, has family-name \"bobson\";"
- "$bobjr isa person;"
- "(parent: $bob, child: $bobjr) isa parenthood;")
-
- tx.commit()
- tx = local_session.transaction().read()
-
- answers = tx.query("match $x isa person, has family-name $f; get;", explain=False)
- for x in answers:
- self.assertFalse(x.has_explanation())
-
- client.keyspaces().delete("query_explain_false")
-
-
- def test_explain_true_explanation_sub_explanation_exists(self):
- with client.session("query_explain_sub_explanation") as local_session:
- tx = local_session.transaction().write()
- tx.query("define family-name sub attribute, value string;"
- "parenthood sub relation, relates parent, relates child;"
- "fatherhood sub parenthood, relates father as parent, relates father-child as child;"
- "person sub entity, has family-name, plays parent, plays child, plays father, plays father-child, has gender;"
- "gender sub attribute, value string; "
- "family-name-inheritence sub rule,"
- "when { (parent: $p, child: $c) isa parenthood; $p has family-name $f; },"
- "then { $c has family-name $f; }; "
- "fatherhood-rule sub rule, "
- "when { $p isa person, has gender \"male\", has family-name $f; $c isa person, has family-name $f; }, "
- "then { (father: $p, father-child: $c) isa fatherhood; };")
- tx.query("insert $bob isa person, has family-name \"bobson\", has gender \"male\";"
- "$bobjr isa person;"
- "(parent: $bob, child: $bobjr) isa parenthood;")
-
- tx.commit()
- tx = local_session.transaction().read()
-
- answers = tx.query("match $m isa fatherhood; get;", explain=True)
- for x in answers:
- if x.has_explanation():
- explanation = x.explanation()
- self.assertIsNotNone(explanation.get_rule())
- sub_answers = explanation.get_answers()
- self.assertTrue(sub_answers[0].has_explanation())
- self.assertIsNotNone(sub_answers[0].explanation())
-
- client.keyspaces().delete("query_explain_sub_explanation")
-
-if __name__ == "__main__":
- with GraknServer():
- unittest.main(verbosity=2)
diff --git a/tests/integration/test_concept.py b/tests/integration/test_concept.py
deleted file mode 100644
index 24ba72eb..00000000
--- a/tests/integration/test_concept.py
+++ /dev/null
@@ -1,786 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you 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 unittest
-from grakn.client import GraknClient, ValueType
-import datetime
-import uuid
-from grakn.exception.GraknError import GraknError
-
-
-from tests.integration.base import test_Base, GraknServer
-
-client = None
-session = None
-
-class test_concept_Base(test_Base):
- """ Sets up DB for use in tests """
-
- @classmethod
- def setUpClass(cls):
- """ Make sure we have some sort of schema and data in DB, only done once """
- super(test_concept_Base, cls).setUpClass()
-
- global client, session
-
- # TODO this is not neat - this is basically emulating a constructor/destructor operation using globals
-
- client = GraknClient("localhost:48555")
- keyspace = "test_" + str(uuid.uuid4()).replace("-", "_")[:8]
- session = client.session(keyspace)
- # temp tx to set up DB, don"t save it
- tx = session.transaction().write()
- try:
- # define parentship roles to test agains
- tx.query("define "
- "parent sub role; "
- "child sub role; "
- "mother sub role; "
- "son sub role; "
- "person sub entity, has age, has gender, plays parent, plays child, plays mother, plays son; "
- "age sub attribute, value long; "
- "gender sub attribute, value string; "
- "parentship sub relation, relates parent, relates child, relates mother, relates son;")
- except GraknError as ce:
- print(ce)
-
- answers = list(tx.query("match $x isa person, has age 20; get;"))
- if len(answers) == 0:
- tx.query("insert $x isa person, has age 20;")
- tx.commit()
-
- @classmethod
- def tearDownClass(cls):
- super(test_concept_Base, cls).tearDownClass()
- global session, client
- session.close()
- # clear the test keyspace
- client.keyspaces().delete(session.keyspace)
- client.close()
-
- def setUp(self):
- global session
- self.tx = session.transaction().write()
- # functions called by `addCleanup` are reliably called independent of test pass or failure
- self.addCleanup(self.cleanupTransaction, self.tx)
-
- def cleanupTransaction(self, tx):
- tx.close()
-
-
-class test_Concept(test_concept_Base):
- """ Test methods available on all Concepts """
-
- def test_delete_schema_types(self):
- car_type = self.tx.put_entity_type("car")
- schema_concept = self.tx.get_schema_concept("car")
- self.assertTrue(schema_concept.is_schema_concept())
- schema_concept.delete()
- none_schema_car = self.tx.get_schema_concept("car")
- self.assertIsNone(none_schema_car, msg="Deletion of car schema type failed")
-
- def test_delete_instance(self):
- car_type = self.tx.put_entity_type("car")
- car = car_type.create()
-
- car.delete()
- none_car = self.tx.get_concept(car.id)
- self.assertIsNone(none_car, msg="Deletion of car instance failed")
-
- def test_re_delete_instance(self):
- car_type = self.tx.put_entity_type("car")
- car = car_type.create()
-
- car.delete()
- none_car = self.tx.get_concept(car.id)
- self.assertIsNone(none_car)
-
- with self.assertRaises(GraknError) as context:
- car.delete()
-
- self.assertTrue("FAILED_PRECONDITION" in str(context.exception))
-
- def test_is_deleted(self):
- car_type = self.tx.put_entity_type("car")
- car = car_type.create()
- self.assertFalse(car.is_deleted())
-
- car.delete()
- self.assertTrue(car.is_deleted())
-
- car2 = car_type.create()
- self.tx.query("match $x isa car; delete $x isa car;")
- self.assertTrue(car2.is_deleted)
-
-
- def test_is_each_schema_type(self):
- car_type = self.tx.put_entity_type("car")
- car = car_type.create()
- self.assertTrue(car.is_entity())
- self.assertFalse(car.is_attribute())
- self.assertFalse(car.is_relation())
-
- rel_type = self.tx.put_relation_type("owner")
- owner = rel_type.create()
- self.assertFalse(owner.is_entity())
- self.assertFalse(owner.is_attribute())
- self.assertTrue(owner.is_relation())
-
- attr_type = self.tx.put_attribute_type("age", ValueType.LONG)
- age = attr_type.create(50)
- self.assertFalse(age.is_entity())
- self.assertTrue(age.is_attribute())
- self.assertFalse(age.is_relation())
-
-class test_SchemaConcept(test_concept_Base):
- """ Test methods available on all SchemaConcepts """
-
- def test_set_label(self):
- """ Get and set labels """
- with self.subTest(i=0):
- # get label
- car_schema_type = self.tx.put_entity_type("car")
- car_type = self.tx.get_schema_concept("car")
- self.assertEqual(car_type.label(), "car")
-
- with self.subTest(i=1):
- # set label
- car_type = self.tx.get_schema_concept("car")
- car_type.label("vehicle")
- vehicle_type = self.tx.get_schema_concept("vehicle")
- self.assertEqual(vehicle_type.label(), "vehicle")
-
- with self.subTest(i=2):
- bike_type = self.tx.get_schema_concept("bike")
- with self.assertRaises(AttributeError):
- bike_type.label("")
- with self.assertRaises(AttributeError):
- bike_type.label(100)
- self.assertIsNone(bike_type)
-
-
- def test_get_sups(self):
- """ Test get super types of a schema concept -- recall a type is supertype of itself always """
- person = self.tx.get_schema_concept("person")
- sups = list(person.sups())
- self.assertEqual(len(sups), 2, msg="person does not have 2 sups")
- sup_labels = [concept.label() for concept in sups]
- self.assertTrue("person" in sup_labels and "entity" in sup_labels)
-
- # check supertype of toplevel schema concepts
- schema_entity = self.tx.get_schema_concept("entity")
- thing_type = schema_entity.sup() # this is Thing
- self.assertEqual(thing_type.base_type, "META_TYPE")
- thing_sup = thing_type.sup()
- self.assertIsNone(thing_sup)
-
- def test_set_sups(self):
- """ Test setting super type of a schema concept """
- human_schema_concept = self.tx.put_entity_type("human")
- male_schema_concept = self.tx.put_entity_type("male")
- human_sup = human_schema_concept.sup()
- self.assertEqual(human_sup.base_type, "ENTITY_TYPE")
-
- male_schema_concept.sup(human_schema_concept)
- sup = male_schema_concept.sup()
- self.assertEqual(sup.label(), "human")
-
- def test_get_subs(self):
- """ Test get sub types of schema concept -- recall a type is a subtype of itself always """
- entity = self.tx.get_schema_concept("entity")
- subs = list(entity.subs())
- self.assertEqual(len(subs), 2, msg="entity does not have 2 subs")
- subs_labels = [sub.label() for sub in subs]
- self.assertTrue('entity' in subs_labels and 'person' in subs_labels)
-
-
-
-
-class test_Type(test_concept_Base):
- """ Tests concept API of things common to Type objects """
-
- def test_is_abstract(self):
- """ Tests get/set of is_abstract on types """
- dog_type = self.tx.put_entity_type("dog")
- with self.subTest(i=0):
- abstract = dog_type.is_abstract()
- self.assertFalse(abstract)
- with self.subTest(i=1):
- dog_type.is_abstract(True)
- abstract = dog_type.is_abstract() #re-retrieve from server
- self.assertTrue(abstract)
- with self.subTest(i=2):
- dog_type.is_abstract(False)
- abstract = dog_type.is_abstract()
- self.assertFalse(abstract)
-
- def test_plays_methods(self):
- """ Test get/set/delete plays ie. roles """
- father = self.tx.put_role("father")
- with self.subTest(i=0):
- person_schema_type = self.tx.get_schema_concept("person")
- person_plays = list(person_schema_type.playing())
- self.assertEqual(len(person_plays), 4)
- with self.subTest(i=1):
- person_schema_type.plays(father)
- updated_person_plays = person_schema_type.playing()
- labels = [role.label() for role in updated_person_plays]
- self.assertEqual(len(labels), 5)
- self.assertTrue("father" in labels)
- with self.subTest(i=2):
- # remove role/plays from person
- person_schema_type.unplay(father)
- updated_person_plays = person_schema_type.playing()
- labels = [role.label() for role in updated_person_plays]
- self.assertEqual(len(labels), 4)
- self.assertFalse("father" in labels)
-
- def test_attributes_methods(self):
- """ Test get/set/delete attributes """
- person = self.tx.get_schema_concept("person")
- haircolor_attr = self.tx.put_attribute_type("haircolor", ValueType.STRING)
- with self.subTest(i=0):
- # get attrs
- current_attrs = person.attributes()
- labels = [attr.label() for attr in current_attrs]
- self.assertEqual(len(labels), 2) # has age, gender to start with
- with self.subTest(i=1):
- # add an attr
- person.has(haircolor_attr)
- new_attrs = person.attributes()
- new_labels = [attr.label() for attr in new_attrs]
- self.assertEqual(len(new_labels), 3)
- self.assertTrue('haircolor' in new_labels)
- with self.subTest(i=2):
- # delete an attrs
- person.unhas(haircolor_attr)
- attrs_fewer = person.attributes()
- labels_fewer = [attr.label() for attr in attrs_fewer]
- self.assertEqual(len(labels_fewer), 2)
- self.assertFalse('haircolor' in labels_fewer)
-
- def test_instances(self):
- """ Test retrieving instances of a type """
- person = self.tx.get_schema_concept("person")
- people = list(person.instances())
- person_inst = person.create()
- people_more = list(person.instances())
- self.assertEqual(len(people_more) - len(people), 1)
-
- def test_key(self):
- """ Test get/set/delete key on Type """
- person_type = self.tx.get_schema_concept("person")
- name_attr_type = self.tx.put_attribute_type('name', ValueType.STRING)
-
- with self.subTest(i=0):
- # check current keys
- keys = list(person_type.keys())
- self.assertEqual(len(keys), 0, "Person has more than 0 keys already")
- with self.subTest(i=1):
- # set a key
- person_type.key(name_attr_type)
- keys = list(person_type.keys())
- self.assertEqual(len(keys), 1)
- self.assertEqual(keys[0].base_type, "ATTRIBUTE_TYPE")
- self.assertEqual(keys[0].label(), 'name')
- with self.subTest(i=2):
- # remove a key
- person_type.unkey(name_attr_type)
- keys = list(person_type.keys())
- self.assertEqual(len(keys), 0)
-
-
-class test_EntityType(test_concept_Base):
-
- def test_create(self):
- person_type = self.tx.get_schema_concept("person")
- person = person_type.create()
- self.assertTrue(person.is_entity())
-
-
-class test_AttributeType(test_concept_Base):
-
- def test_create(self):
- str_attr_type = self.tx.put_attribute_type("firstname", ValueType.STRING)
- john = str_attr_type.create("john")
- self.assertTrue(john.is_attribute())
- self.assertEqual(john.value(), "john")
-
- bool_attr_type = self.tx.put_attribute_type("employed", ValueType.BOOLEAN)
- employed = bool_attr_type.create(True)
- self.assertEqual(employed.value(), True)
-
- double_attr_type = self.tx.put_attribute_type("length", ValueType.DOUBLE)
- one = double_attr_type.create(1.0)
- self.assertEqual(one.value(), 1.0)
-
- def test_value_type(self):
- str_attr_type = self.tx.put_attribute_type("firstname", ValueType.STRING)
- self.assertEqual(str_attr_type.value_type(), ValueType.STRING)
-
- bool_attr_type = self.tx.put_attribute_type("employed", ValueType.BOOLEAN)
- self.assertEqual(bool_attr_type.value_type(), ValueType.BOOLEAN)
-
- double_attr_type = self.tx.put_attribute_type("length", ValueType.DOUBLE)
- self.assertEqual(double_attr_type.value_type(), ValueType.DOUBLE)
-
- long_attr_type = self.tx.put_attribute_type("randomint", ValueType.LONG)
- self.assertEqual(long_attr_type.value_type(), ValueType.LONG)
-
- def test_attribute(self):
- """ Test retrieve attribute instances """
-
- name = self.tx.put_attribute_type("name", ValueType.STRING)
- john = name.create("john")
-
- with self.subTest(i=0):
- # retrieve existing attr client
- retrieved_john = name.attribute("john")
- self.assertEqual(retrieved_john.value(), john.value())
- self.assertTrue(retrieved_john.is_attribute())
- with self.subTest(i=1):
- # retrieve nonexistant attr client
- retrieved_none = name.attribute("nobody")
- self.assertIsNone(retrieved_none)
-
- def test_regex(self):
- """ Test get/set regex """
- attr_type = self.tx.put_attribute_type("dogbadness", ValueType.STRING)
-
- empty_regex = attr_type.regex()
- self.assertEqual(len(empty_regex), 0, msg="Unset regex does not have length 0")
-
- attr_type.regex("(good|bad)-dog")
- regex = attr_type.regex()
- self.assertEqual(regex, "(good|bad)-dog")
-
-
-class test_RelationType(test_concept_Base):
-
- def test_create(self):
- rel_type = self.tx.put_relation_type("owner")
- rel = rel_type.create()
- self.assertTrue(rel.is_relation())
- self.assertTrue(rel_type.is_relation_type())
-
- def test_relates(self):
- """ Test get/relate/unrelate roles for a relation type """
- ownership = self.tx.put_relation_type("ownership")
- role_owner = self.tx.put_role("owner")
- role_owned = self.tx.put_role("owned")
-
- with self.subTest(i=0):
- # currently no roles in the new relation
- roles = list(ownership.roles())
- self.assertEqual(len(roles), 0)
- with self.subTest(i=1):
- # set roles in relation
- ownership.relates(role_owner)
- ownership.relates(role_owned)
- roles = list(ownership.roles())
- self.assertEqual(len(roles), 2)
- with self.subTest(i=2):
- # unrelate a role
- ownership.unrelate(role_owned)
- roles = list(ownership.roles())
- self.assertEqual(len(roles), 1)
- self.assertEqual(roles[0].base_type, "ROLE")
-
-
-class test_Rule(test_concept_Base):
-
- def test_when_then(self):
- """ Test get valid when/then """
- label = "genderizedparentship"
- when = "{ (parent: $p, child: $c) isa parentship; $c has gender \"male\"; $p has gender \"female\"; };"
- then = "{ (mother: $p, son: $c) isa parentship; };"
- rule = self.tx.put_rule(label, when, then)
-
- self.assertEqual(rule.get_when(), when)
- self.assertEqual(rule.get_then(), then)
-
- def test_none_when_then(self):
- """ Test get when/then for rule with null when/then """
- rule = self.tx.get_schema_concept('rule')
- self.assertIsNone(rule.get_when())
- self.assertIsNone(rule.get_then())
-
-
-class test_Role(test_concept_Base):
-
- def test_relations(self):
- """ Test retrieving relations of a role """
- # parent role, parentship already exist
- result = [ans.get("x") for ans in self.tx.query("match $x type parent; get;")]
- parent_role = result[0].as_remote(self.tx)
- self.assertEqual(parent_role.base_type, "ROLE")
-
- relations = list(parent_role.relations())
- self.assertEqual(len(relations), 1)
- self.assertEqual(relations[0].base_type, "RELATION_TYPE")
- self.assertEqual(relations[0].label(), "parentship")
-
- def test_players(self):
- """ Test retrieving entity types playing this role """
- result = [ans.get("x") for ans in self.tx.query("match $x type parent; get;")]
- parent_role = result[0].as_remote(self.tx)
- self.assertEqual(parent_role.base_type, "ROLE")
-
- entity_types = list(parent_role.players())
- self.assertEqual(len(entity_types), 1)
- self.assertEqual(entity_types[0].base_type, "ENTITY_TYPE")
- self.assertEqual(entity_types[0].label(), "person")
-
-
-class test_Thing(test_concept_Base):
-
- def test_is_inferred(self):
- person_type = self.tx.get_schema_concept("person")
- person = person_type.create()
- self.assertFalse(person.is_inferred())
-
- def test_type(self):
- person_type = self.tx.get_schema_concept("person")
- person = person_type.create()
- p_type = person.type()
- self.assertEqual(p_type.id, person_type.id) # same schema concept
- self.assertTrue(p_type.is_type())
-
- def test_relations(self):
- """ Test retrieve relations narrowed optionally by roles """
- # create a first relation
- sibling_type = self.tx.put_relation_type('sibling')
- brother_role = self.tx.put_role("brother")
- sibling_type.relates(brother_role)
- person = self.tx.get_schema_concept("person")
-
- # create a second relation
- ownership_type = self.tx.put_relation_type("ownership")
- owner_role = self.tx.put_role("owner")
- ownership_type.relates(owner_role)
- person.plays(owner_role)
-
- # connect entities/relation instances
- sibling = sibling_type.create()
- ownership = ownership_type.create()
- son = person.create()
- sibling.assign(brother_role, son) # assign son to sibling rel
- ownership.assign(owner_role, son) # attach son to owner rel
-
- # retrieve all relations
- rels = list(son.relations())
- self.assertEqual(len(rels), 2)
- rel_ids = [rel.id for rel in rels]
- self.assertTrue(sibling.id in rel_ids and ownership.id in rel_ids)
-
-
- # retrieve filtered by only the owner role
- filtered_rels = list(son.relations(owner_role))
- self.assertEqual(len(filtered_rels), 1)
- self.assertEqual(filtered_rels[0].id, ownership.id)
-
- def test_roles(self):
- # create a relation
- ownership_type = self.tx.put_relation_type("ownership")
- owner_role = self.tx.put_role("owner")
- ownership_type.relates(owner_role)
- person_type = self.tx.get_schema_concept("person")
- person_type.plays(owner_role)
-
- # connect entities/relation instances
- ownership = ownership_type.create()
- person = person_type.create()
- ownership.assign(owner_role, person) # attach son to owner rel
-
- roles = list(person.roles())
- self.assertEqual(len(roles), 1)
- self.assertEqual(roles[0].id, owner_role.id)
-
-
- def test_has_unhas_attributes(self):
- """ Test has/unhas/get attributes """
- person_type = self.tx.get_schema_concept("person")
- name_attr_type = self.tx.put_attribute_type("name", ValueType.STRING)
- person_type.has(name_attr_type)
- person = person_type.create()
- attr_john = name_attr_type.create("john")
- person.has(attr_john)
-
- attrs = list(person.attributes())
- self.assertEqual(len(attrs), 1)
- self.assertEqual(attrs[0].id, attr_john.id)
-
- person.unhas(attr_john)
- empty_attrs = list(person.attributes())
- self.assertEqual(len(empty_attrs), 0)
-
- def test_attributes(self):
- """ Test retrieve attrs optionally narrowed by types """
- person_type = self.tx.get_schema_concept("person")
- name_attr = self.tx.put_attribute_type("name", ValueType.STRING)
- foo_attr = self.tx.put_attribute_type("foo", ValueType.BOOLEAN)
- bar_attr = self.tx.put_attribute_type("bar", ValueType.LONG)
-
- person_type.has(name_attr)
- person_type.has(foo_attr)
-
- person = person_type.create()
- name = name_attr.create("john")
- foo = foo_attr.create(False)
- person.has(name)
- person.has(foo)
-
- attrs = list(person.attributes())
- self.assertEqual(len(attrs), 2)
- for attr in attrs:
- self.assertTrue(attr.is_attribute())
-
- #filtered attrs
- attrs = list(person.attributes(name_attr))
- self.assertEqual(len(attrs), 1)
- self.assertTrue(attrs[0].is_attribute())
- self.assertEqual(attrs[0].id, name.id)
- attrs = list(person.attributes(name_attr, foo_attr))
- self.assertEqual(len(attrs), 2)
-
- #nonexistant filtering
- attrs = list(person.attributes(bar_attr)) # not attached
- self.assertEqual(len(attrs), 0)
-
- def test_keys(self):
- """ Test retrieving keys optionally filtered by attribute types """
- person_type = self.tx.get_schema_concept("person")
- name_type = self.tx.put_attribute_type("name", ValueType.STRING)
- surname_type = self.tx.put_attribute_type("surname", ValueType.STRING)
- person_type.key(name_type)
- person_type.has(surname_type)
-
- name = name_type.create("john")
- surname = surname_type.create("lennon")
- person = person_type.create()
- person.has(name)
- person.has(surname)
-
- keys = list(person.keys())
- self.assertEqual(len(keys), 1)
- self.assertEqual(keys[0].id, name.id)
-
- filtered_keys = list(person.keys(name_type, surname_type))
- self.assertEqual(len(filtered_keys), 1)
- self.assertEqual(filtered_keys[0].id, name.id)
-
- empty_keys = list(person.keys(surname_type))
- self.assertEqual(len(empty_keys), 0)
-
-
-class test_Attribute(test_concept_Base):
-
- def test_value(self):
- """ Get attribute value """
- double_attr_type = self.tx.put_attribute_type("length", ValueType.DOUBLE)
- double = double_attr_type.create(43.1)
- self.assertEqual(double.value(), 43.1)
-
- def test_get_date_value(self):
- date_type = self.tx.put_attribute_type("birthdate", ValueType.DATETIME)
- person_type = self.tx.get_schema_concept("person")
- person_type.has(date_type)
- concepts = [ans.get("x") for ans in self.tx.query("insert $x isa person, has birthdate 2018-08-06;")]
- person = concepts[0].as_remote(self.tx)
- attrs_iter = person.attributes()
- for attr_concept in attrs_iter:
- # pick out the birthdate
- if attr_concept.type().label() == "birthdate":
- date = attr_concept.value()
- self.assertIsInstance(date, datetime.datetime)
- self.assertEqual(date.year, 2018)
- self.assertEqual(date.month, 8)
- self.assertEqual(date.day, 6)
- return
-
- def test_set_date_value(self):
- date_type = self.tx.put_attribute_type("birthdate", ValueType.DATETIME)
- test_date = datetime.datetime(year=2018, month=6, day=6)
- date_attr_inst = date_type.create(test_date)
- value = date_attr_inst.value() # retrieve from server
- self.assertIsInstance(value, datetime.datetime)
- self.assertEqual(value.timestamp(), test_date.timestamp())
-
- def test_owners(self):
- """ Test retrieving entities that have an attribute """
- person_type = self.tx.get_schema_concept("person")
- animal_type = self.tx.put_entity_type("animal")
- name_type = self.tx.put_attribute_type("name", ValueType.STRING)
- person_type.has(name_type)
- animal_type.has(name_type)
-
- person = person_type.create()
- animal = animal_type.create()
- john = name_type.create("john")
-
- person.has(john)
- animal.has(john)
-
- owners = list(john.owners())
- self.assertEqual(len(owners), 2)
- labels = [x.id for x in owners]
- self.assertTrue(person.id in labels and animal.id in labels)
-
-
-class test_Relation(test_concept_Base):
-
- def test_role_players_2_roles_1_player(self):
- """ Test role_players_map and role_players with 2 roles and 1 player each """
- parentship_type = self.tx.get_schema_concept("parentship")
- person_type = self.tx.get_schema_concept("person")
- parent_role = self.tx.get_schema_concept("parent")
- child_role = self.tx.get_schema_concept("child")
-
- parent = person_type.create()
- child = person_type.create()
- parentship = parentship_type.create()
-
- parentship.assign(parent_role, parent)
- parentship.assign(child_role, child)
-
- role_players_map = parentship.role_players_map()
- self.assertEqual(len(role_players_map.keys()), 2)
- for role in role_players_map:
- players_list = role_players_map[role]
- self.assertEqual(len(players_list), 1)
- self.assertTrue(role.is_role())
-
- role_players = list(parentship.role_players())
- self.assertEqual(len(role_players), 2)
-
- def test_role_players_1_role_2_players(self):
- parentship_type = self.tx.get_schema_concept("parentship")
- person_type = self.tx.get_schema_concept("person")
- parent_role = self.tx.get_schema_concept("parent")
-
- parent = person_type.create()
- another_parent = person_type.create()
- parentship = parentship_type.create()
-
- parentship.assign(parent_role, parent)
- parentship.assign(parent_role, another_parent)
-
- role_players_map = parentship.role_players_map()
- self.assertEqual(len(role_players_map.keys()), 1)
- for role in role_players_map:
- players_list = role_players_map[role]
- self.assertEqual(len(players_list), 2)
- self.assertTrue(role.is_role())
-
- role_players = list(parentship.role_players())
- self.assertEqual(len(role_players), 2)
-
- def test_role_players_2_roles_same_player(self):
- parentship_type = self.tx.get_schema_concept("parentship")
- person_type = self.tx.get_schema_concept("person")
- parent_role = self.tx.get_schema_concept("parent")
- child_role = self.tx.get_schema_concept("child")
-
- self_parent = person_type.create()
- parentship = parentship_type.create()
-
- parentship.assign(parent_role, self_parent)
- parentship.assign(child_role, self_parent)
-
- role_players_map = parentship.role_players_map()
- self.assertEqual(len(role_players_map.keys()), 2)
- for role in role_players_map:
- players_list = role_players_map[role]
- self.assertEqual(len(players_list), 1)
- self.assertTrue(role.is_role())
-
- role_players = list(parentship.role_players())
- self.assertEqual(len(role_players), 2)
- self.assertTrue(role_players[0].is_thing())
-
- def test_role_players_1_role_same_player(self):
- parentship_type = self.tx.get_schema_concept("parentship")
- person_type = self.tx.get_schema_concept("person")
- parent_role = self.tx.get_schema_concept("parent")
-
- self_parent = person_type.create()
- parentship = parentship_type.create()
-
- parentship.assign(parent_role, self_parent)
- parentship.assign(parent_role, self_parent)
-
- role_players_map = parentship.role_players_map()
- self.assertEqual(len(role_players_map.keys()), 1)
- for role in role_players_map:
- players_list = role_players_map[role]
- self.assertEqual(len(players_list), 2)
- self.assertTrue(role.is_role())
-
- role_players = list(parentship.role_players())
- self.assertEqual(len(role_players), 2)
- self.assertTrue(role_players[0].is_thing())
-
- def test_assign_unassign(self):
- parentship_type = self.tx.get_schema_concept("parentship")
- person_type = self.tx.get_schema_concept("person")
- parent_role = self.tx.get_schema_concept("parent")
-
- person = person_type.create()
- parentship = parentship_type.create()
-
- empty_role_players = list(parentship.role_players())
- self.assertEqual(len(empty_role_players), 0)
-
- parentship.assign(parent_role, person)
- role_players = list(parentship.role_players())
- self.assertEqual(len(role_players), 1)
- self.assertEqual(role_players[0].id, person.id)
-
- parentship.unassign(parent_role, person)
- self.assertTrue(parentship.is_deleted())
-
-
- def test_role_players_filtered_by_role(self):
- parentship_type = self.tx.get_schema_concept("parentship")
- person_type = self.tx.get_schema_concept("person")
- parent_role = self.tx.get_schema_concept("parent")
- child_role = self.tx.get_schema_concept("child")
-
- parent = person_type.create()
- child = person_type.create()
- parentship = parentship_type.create()
- parentship.assign(parent_role, parent)
- parentship.assign(child_role, child)
-
- # no filter
- role_players = list(parentship.role_players())
- self.assertEqual(len(role_players), 2)
- # single filter
- filtered_role_players = list(parentship.role_players(child_role))
- self.assertEqual(len(filtered_role_players), 1)
- self.assertEqual(filtered_role_players[0].id, child.id)
-
- # allow both
- double_filter_role_players = list(parentship.role_players(child_role, parent_role))
- self.assertEqual(len(double_filter_role_players), 2)
-
-
-if __name__ == "__main__":
- with GraknServer():
- unittest.main(verbosity=2)
diff --git a/tests/integration/test_keyspace.py b/tests/integration/test_keyspace.py
deleted file mode 100644
index ff808734..00000000
--- a/tests/integration/test_keyspace.py
+++ /dev/null
@@ -1,59 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you 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 unittest
-from grakn.client import GraknClient
-from tests.integration.base import test_Base, GraknServer
-
-client = None
-session = None
-
-class test_Keyspace(test_Base):
- @classmethod
- def setUpClass(cls):
- super(test_Keyspace, cls).setUpClass()
- global client, session
- client = GraknClient("localhost:48555")
- session = client.session("keyspacetest")
-
- @classmethod
- def tearDownClass(cls):
- super(test_Keyspace, cls).tearDownClass()
- global client, session
- session.close()
- client.close()
-
- def test_retrieve_delete(self):
- """ Test retrieving and deleting a specific keyspace """
-
- tx = session.transaction().write()
- tx.close()
-
- keyspaces = client.keyspaces().retrieve()
- self.assertGreater(len(keyspaces), 0)
- self.assertTrue('keyspacetest' in keyspaces)
-
- client.keyspaces().delete('keyspacetest')
- post_delete_keyspaces = client.keyspaces().retrieve()
- self.assertFalse('keyspacetest' in post_delete_keyspaces)
-
-
-if __name__ == "__main__":
- with GraknServer():
- unittest.main(verbosity=2)