PyStellarDB 0.13.5__py2.py3-none-any.whl → 1.0__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: PyStellarDB
3
- Version: 0.13.5
3
+ Version: 1.0
4
4
  Summary: Python interface to StellarDB
5
5
  Home-page: https://github.com/WarpCloud/PyStellarDB
6
6
  Author: Zhiping Wang
@@ -10,25 +10,26 @@ Classifier: Intended Audience :: Developers
10
10
  Classifier: License :: OSI Approved :: Apache Software License
11
11
  Classifier: Operating System :: OS Independent
12
12
  Classifier: Topic :: Database :: Front-Ends
13
- Requires-Python: >=2.7
13
+ Requires-Python: >=3.6
14
14
  License-File: LICENSE
15
15
  Requires-Dist: future
16
16
  Requires-Dist: python-dateutil
17
- Requires-Dist: pyhive
18
- Requires-Dist: sasl
19
- Requires-Dist: thrift
20
- Requires-Dist: thrift-sasl >=0.3.0
21
- Provides-Extra: hive
22
- Requires-Dist: sasl >=0.2.1 ; extra == 'hive'
23
- Requires-Dist: thrift >=0.10.0 ; extra == 'hive'
24
- Provides-Extra: kerberos
25
- Requires-Dist: requests-kerberos >=0.12.0 ; extra == 'kerberos'
26
- Provides-Extra: presto
27
- Requires-Dist: requests >=1.0.0 ; extra == 'presto'
17
+ Requires-Dist: pyhive[hive_pure_sasl]
18
+ Requires-Dist: thrift>=0.10.0
28
19
  Provides-Extra: pyspark
29
- Requires-Dist: pyspark >=2.4.0 ; extra == 'pyspark'
30
- Provides-Extra: sqlalchemy
31
- Requires-Dist: sqlalchemy >=1.3.0 ; extra == 'sqlalchemy'
20
+ Requires-Dist: pyspark>=2.4.0; extra == "pyspark"
21
+ Provides-Extra: kerberos
22
+ Requires-Dist: kerberos>=1.3.0; extra == "kerberos"
23
+ Dynamic: author
24
+ Dynamic: author-email
25
+ Dynamic: classifier
26
+ Dynamic: description
27
+ Dynamic: home-page
28
+ Dynamic: license
29
+ Dynamic: provides-extra
30
+ Dynamic: requires-dist
31
+ Dynamic: requires-python
32
+ Dynamic: summary
32
33
 
33
34
  PyStellarDB
34
35
  ===========
@@ -60,6 +61,7 @@ PLAIN Mode (No security is configured)
60
61
  ---------------------------------------
61
62
  .. code-block:: python
62
63
 
64
+ """version < 1.0 """
63
65
  from pystellardb import stellar_hive
64
66
 
65
67
  conn = stellar_hive.StellarConnection(host="localhost", port=10000, graph_name='pokemon')
@@ -70,11 +72,19 @@ PLAIN Mode (No security is configured)
70
72
 
71
73
  print cur.fetchall()
72
74
 
75
+ """version >= 1.0 """
76
+ from pystellardb import Connection, Graph
77
+ conn = stellar_hive.StellarConnection(host="localhost", port=10000)
78
+ graph = Graph('pokemon', conn)
79
+ query_result = graph.execute('match p = (a)-[f]->(b) return a,f,b limit 1')
80
+ for row in query_result:
81
+ print(row[0].toJSON(), row[1].toJSON(), row[2].toJSON())
73
82
 
74
83
  LDAP Mode
75
84
  ---------
76
85
  .. code-block:: python
77
86
 
87
+ """version < 1.0 """
78
88
  from pystellardb import stellar_hive
79
89
 
80
90
  conn = stellar_hive.StellarConnection(host="localhost", port=10000, username='hive', password='123456', auth='LDAP', graph_name='pokemon')
@@ -85,6 +95,14 @@ LDAP Mode
85
95
 
86
96
  print cur.fetchall()
87
97
 
98
+ """version >= 1.0 """
99
+ from pystellardb import Connection, Graph
100
+ conn = stellar_hive.StellarConnection(host="localhost", port=10000, username='hive', password='123456', auth='LDAP')
101
+ graph = Graph('pokemon', conn)
102
+ query_result = graph.execute('match p = (a)-[f]->(b) return a,f,b limit 1')
103
+ for row in query_result:
104
+ print(row[0].toJSON(), row[1].toJSON(), row[2].toJSON())
105
+
88
106
 
89
107
  Kerberos Mode
90
108
  -------------
@@ -96,6 +114,12 @@ Kerberos Mode
96
114
  # In Linux: kinit -kt FILE_PATH_OF_KEYTABL PRINCIPAL_NAME
97
115
  # In Mac: kinit -t FILE_PATH_OF_KEYTABL -f PRINCIPAL_NAME
98
116
 
117
+ # Run with Kerberos path environment variables
118
+ # ENV KRB5_CONFIG=/etc/krb5.conf
119
+ # ENV KRB5_CLIENT_KTNAME=/etc/krb5.keytab
120
+ # ENV KRB5_KTNAME=/etc/krb5.keytab
121
+
122
+ """version < 1.0 """
99
123
  from pystellardb import stellar_hive
100
124
 
101
125
  conn = stellar_hive.StellarConnection(host="localhost", port=10000, kerberos_service_name='hive', auth='KERBEROS', graph_name='pokemon')
@@ -106,6 +130,13 @@ Kerberos Mode
106
130
 
107
131
  print cur.fetchall()
108
132
 
133
+ """version >= 1.0 """
134
+ from pystellardb import Connection, Graph
135
+ conn = stellar_hive.StellarConnection(host="localhost", port=10000, kerberos_service_name='hive', auth='KERBEROS')
136
+ graph = Graph('pokemon', conn)
137
+ query_result = graph.execute('match p = (a)-[f]->(b) return a,f,b limit 1')
138
+ for row in query_result:
139
+ print(row[0].toJSON(), row[1].toJSON(), row[2].toJSON())
109
140
 
110
141
  Execute Hive Query
111
142
  ------------------
@@ -123,6 +154,7 @@ Execute Graph Query and change to a PySpark RDD object
123
154
  ------------------------------------------------------
124
155
  .. code-block:: python
125
156
 
157
+ """version < 1.0 """
126
158
  from pyspark import SparkContext
127
159
  from pystellardb import stellar_hive
128
160
 
@@ -140,6 +172,22 @@ Execute Graph Query and change to a PySpark RDD object
140
172
 
141
173
  rdd.map(lambda x: (x[0].toJSON(), x[1].toJSON(), x[2].toJSON())).foreach(f)
142
174
 
175
+ """version >= 1.0 """
176
+ from pyspark import SparkContext
177
+ from pystellardb import Connection, Graph
178
+
179
+ sc = SparkContext("local", "Demo App")
180
+
181
+
182
+ conn = stellar_hive.StellarConnection(host="localhost", port=10000)
183
+ graph = Graph('pokemon', conn)
184
+ query_result = graph.execute('match p = (a)-[f]->(b) return a,f,b limit 1')
185
+ rdd = query_result.toRDD(sc)
186
+
187
+ def f(x): print(x)
188
+
189
+ rdd.map(lambda x: (x[0].toJSON(), x[1].toJSON(), x[2].toJSON())).foreach(f)
190
+
143
191
  # Every line of this query is in format of Tuple(VertexObject, EdgeObject, VertexObject)
144
192
  # Vertex and Edge object has a function of toJSON() which can print the object in JSON format
145
193
 
@@ -171,24 +219,24 @@ Dependencies
171
219
  Required:
172
220
  ------------
173
221
 
174
- - Python 2.7+ / Python 3
222
+ - Python 3.6+
175
223
 
176
- System SASL
177
- ------------
224
+ System SASL(Depricated since 1.0):
225
+ ----------------------------------
178
226
 
179
227
  Ubuntu:
180
228
 
181
229
  .. code-block:: bash
182
230
 
183
231
  apt-get install libsasl2-dev libsasl2-2 libsasl2-modules-gssapi-mit
184
- apt-get install python-dev gcc #Update python and gcc if needed
232
+ apt-get install python3-dev gcc #Update python and gcc if needed
185
233
 
186
234
  RHEL/CentOS:
187
235
 
188
236
  .. code-block:: bash
189
237
 
190
238
  yum install cyrus-sasl-md5 cyrus-sasl-plain cyrus-sasl-gssapi cyrus-sasl-devel
191
- yum install gcc-c++ python-devel.x86_64 #Update python and gcc if needed
239
+ yum install gcc-c++ python3-devel.x86_64 #Update python and gcc if needed
192
240
 
193
241
  # if pip3 install fails with a message like 'Can't connect to HTTPS URL because the SSL module is not available'
194
242
  # you may need to update ssl & reinstall python
@@ -0,0 +1,14 @@
1
+ pystellardb/__init__.py,sha256=LHYhg_z_zEX3YLetkbxcZrlJDjQYN5jS9W7RlUck7Wg,285
2
+ pystellardb/_version.py,sha256=bsy7XZ8ffg4i9t7-lI7_arjuL7Y5XBvn7aTm4u1Dirw,495
3
+ pystellardb/graph_types.py,sha256=XbtPebhaRV7OimW-GxFJGSOcp-OnzWrlIe9M7Xv3sbU,14032
4
+ pystellardb/sasl_compat.py,sha256=HLS5PC2T4-E9FCknFZDVGSRpGdrOnKgrrHeJNMtGNPU,1322
5
+ pystellardb/stellar_hive.py,sha256=-6sAQtWbAPhMFUetBAAjkrIqImCYs8hpUvDmCTfdk7I,14964
6
+ pystellardb/stellar_rdd.py,sha256=TYwsWYeCxfOliGq1kV3ArNXdye55cKWZF7s9M9nDdt4,1324
7
+ pystellardb/v2/__init__.py,sha256=12jK5F3bAmNnvxUpoUhgSY4laBfiNiU2GQVM_PkZ8hA,142
8
+ pystellardb/v2/connection.py,sha256=Ma1AXo70QNuoaeJkL-D5rAQCV79knfWFFmpi3Ej2aCE,10762
9
+ pystellardb/v2/database.py,sha256=iI0XF5sgyPiRXeXDJOJsfYrC5K46671k3liWotL-7HA,3147
10
+ PyStellarDB-1.0.dist-info/LICENSE,sha256=1qDFxrywejs7xNBfOr6T-7lOuqDgSNIES77kTYege3w,560
11
+ PyStellarDB-1.0.dist-info/METADATA,sha256=1MDEIEp7fmQKO6vLN3x071v2lB_uH2rIrzxoWXkoAWA,11221
12
+ PyStellarDB-1.0.dist-info/WHEEL,sha256=9Hm2OB-j1QcCUq9Jguht7ayGIIZBRTdOXD1qg9cCgPM,109
13
+ PyStellarDB-1.0.dist-info/top_level.txt,sha256=DRk-SeGVCdVAzv2CwFmdu75Yo7DgjUA3Hpu-9l8qPuU,12
14
+ PyStellarDB-1.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (70.1.1)
2
+ Generator: setuptools (75.8.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py2-none-any
5
5
  Tag: py3-none-any
pystellardb/__init__.py CHANGED
@@ -1,4 +1,17 @@
1
1
 
2
2
  from ._version import get_versions
3
+ from .v2 import Connection, Graph, CypherResult
4
+
5
+
3
6
  __version__ = get_versions()['version']
4
7
  del get_versions
8
+
9
+ __all__ = [
10
+ '__version__',
11
+ 'stellar_hive',
12
+ 'stellar_rdd',
13
+ 'graph_types',
14
+ 'Connection',
15
+ 'Graph',
16
+ 'CypherResult',
17
+ ]
pystellardb/_version.py CHANGED
@@ -8,11 +8,11 @@ import json
8
8
 
9
9
  version_json = '''
10
10
  {
11
- "date": "2024-12-17T19:30:54+0800",
11
+ "date": "2025-05-06T17:47:56+0800",
12
12
  "dirty": false,
13
13
  "error": null,
14
- "full-revisionid": "edb05dd3d51f0a5af260e6cd442163058a81db20",
15
- "version": "0.13.5"
14
+ "full-revisionid": "76d9b0b752e5e453e3046d5864750c4e3ad85b36",
15
+ "version": "1.0"
16
16
  }
17
17
  ''' # END VERSION_JSON
18
18
 
@@ -0,0 +1,49 @@
1
+ from __future__ import absolute_import
2
+
3
+ from puresasl.client import SASLClient, SASLError
4
+ from contextlib import contextmanager
5
+
6
+ @contextmanager
7
+ def error_catcher(self, Exc = Exception):
8
+ try:
9
+ self.error = None
10
+ yield
11
+ except Exc as e:
12
+ self.error = str(e)
13
+
14
+
15
+ class PureSASLClient(SASLClient):
16
+ def __init__(self, *args, **kwargs):
17
+ self.error = None
18
+ super(PureSASLClient, self).__init__(*args, **kwargs)
19
+
20
+ def start(self, mechanism):
21
+ with error_catcher(self, SASLError):
22
+ if isinstance(mechanism, list):
23
+ self.choose_mechanism(mechanism)
24
+ else:
25
+ self.choose_mechanism([mechanism])
26
+ return True, self.mechanism, self.process()
27
+ # else
28
+ return False, mechanism, None
29
+
30
+ def encode(self, incoming):
31
+ with error_catcher(self):
32
+ return True, self.unwrap(incoming)
33
+ # else
34
+ return False, None
35
+
36
+ def decode(self, outgoing):
37
+ with error_catcher(self):
38
+ return True, self.wrap(outgoing)
39
+ # else
40
+ return False, None
41
+
42
+ def step(self, challenge=None):
43
+ with error_catcher(self):
44
+ return True, self.process(challenge)
45
+ # else
46
+ return False, None
47
+
48
+ def getError(self):
49
+ return self.error
@@ -9,8 +9,9 @@ from __future__ import absolute_import
9
9
  from __future__ import unicode_literals
10
10
 
11
11
  from pyhive import hive
12
- from pystellardb import graph_types
13
- from pystellardb import stellar_rdd
12
+ from . import graph_types
13
+ from . import stellar_rdd
14
+ from .sasl_compat import PureSASLClient
14
15
 
15
16
  from TCLIService import TCLIService
16
17
  from TCLIService import ttypes
@@ -102,12 +103,10 @@ class StellarConnection(object):
102
103
  self._transport = thrift.transport.TTransport.TBufferedTransport(
103
104
  socket)
104
105
  elif auth in ('LDAP', 'KERBEROS', 'NONE', 'CUSTOM'):
105
- # Defer import so package dependency is optional
106
- import sasl
107
106
  import thrift_sasl
108
107
 
109
108
  if auth == 'KERBEROS':
110
- # KERBEROS mode in hive.server2.authentication is GSSAPI in sasl library
109
+ # KERBEROS mode in hive.server2.authentication is GSSAPI in SASL
111
110
  sasl_auth = 'GSSAPI'
112
111
  else:
113
112
  sasl_auth = 'PLAIN'
@@ -116,20 +115,15 @@ class StellarConnection(object):
116
115
  password = 'x'
117
116
 
118
117
  def sasl_factory():
119
- sasl_client = sasl.Client()
120
- sasl_client.setAttr('host', host)
121
118
  if sasl_auth == 'GSSAPI':
122
- sasl_client.setAttr('service', kerberos_service_name)
119
+ sasl_client = PureSASLClient(host, mechanism="GSSAPI", service=kerberos_service_name)
123
120
  elif sasl_auth == 'PLAIN':
124
- sasl_client.setAttr('username', username)
125
- sasl_client.setAttr('password', password)
121
+ sasl_client = PureSASLClient(host, mechanism="PLAIN", username=username, password=password)
126
122
  else:
127
- raise AssertionError
128
- sasl_client.init()
123
+ raise AssertionError("Unsupported SASL mechanism")
129
124
  return sasl_client
130
125
 
131
- self._transport = thrift_sasl.TSaslClientTransport(
132
- sasl_factory, sasl_auth, socket)
126
+ self._transport = thrift_sasl.TSaslClientTransport(sasl_factory, sasl_auth, socket)
133
127
  else:
134
128
  # All HS2 config options:
135
129
  # https://cwiki.apache.org/confluence/display/Hive/Setting+Up+HiveServer2#SettingUpHiveServer2-Configuration
@@ -174,9 +168,12 @@ class StellarConnection(object):
174
168
  schemaInJson)
175
169
 
176
170
  # get schema from data
177
- cursor.execute('manipulate graph {} get_schema_from_data'.format(graph_name))
178
- self._graph_schema_from_data = cursor.fetchone()[0]
179
- self._graph_schema_from_data = json.loads(self._graph_schema_from_data)
171
+ try:
172
+ cursor.execute('manipulate graph {} get_schema_from_data'.format(graph_name))
173
+ self._graph_schema_from_data = cursor.fetchone()[0]
174
+ self._graph_schema_from_data = json.loads(self._graph_schema_from_data)
175
+ except:
176
+ pass
180
177
  else:
181
178
  assert response.serverProtocolVersion == protocol_version, \
182
179
  "Unable to handle protocol version {}".format(response.serverProtocolVersion)
@@ -0,0 +1,8 @@
1
+ from .connection import Connection, CypherResult
2
+ from .database import Graph
3
+
4
+ __all__ = [
5
+ "Graph",
6
+ "Connection",
7
+ "CypherResult",
8
+ ]
@@ -0,0 +1,276 @@
1
+ from __future__ import absolute_import
2
+ from __future__ import unicode_literals
3
+
4
+ from types import TracebackType
5
+ from typing import Any, Union
6
+ from pyhive import hive
7
+ from pyhive.exc import *
8
+ from TCLIService import TCLIService
9
+ from TCLIService import ttypes
10
+
11
+ import thrift.protocol.TBinaryProtocol
12
+ import thrift.transport.TSocket
13
+ import thrift.transport.TTransport
14
+ import getpass
15
+ import logging
16
+ import json
17
+ import sys
18
+
19
+ from pystellardb.stellar_rdd import transformToRDD
20
+ from pystellardb.graph_types import Vertex, Edge, Path, GraphSchema
21
+ from pystellardb.sasl_compat import PureSASLClient
22
+
23
+ __logger = logging.getLogger(__name__)
24
+
25
+ class CypherResult(hive.Cursor):
26
+ """
27
+ QueryResult class for handling query results.
28
+ """
29
+
30
+ def __init__(self, connection: Any, graph_schema: GraphSchema, arraysize: int = 1000):
31
+ super(CypherResult, self).__init__(connection, arraysize)
32
+ self._graph_schema = graph_schema
33
+ self._column_comments = []
34
+
35
+ @property
36
+ def description(self):
37
+ """This read-only attribute is a sequence of 7-item sequences.
38
+
39
+ Each of these sequences contains information describing one result column:
40
+
41
+ - name
42
+ - type_code
43
+ - display_size (None in current implementation)
44
+ - internal_size (None in current implementation)
45
+ - precision (None in current implementation)
46
+ - scale (None in current implementation)
47
+ - null_ok (always True in current implementation)
48
+
49
+ This attribute will be ``None`` for operations that do not return rows or if the cursor has
50
+ not had an operation invoked via the :py:meth:`execute` method yet.
51
+
52
+ The ``type_code`` can be interpreted by comparing it to the Type Objects specified in the
53
+ section below.
54
+ """
55
+ if self._operationHandle is None or not self._operationHandle.hasResultSet:
56
+ return None
57
+ if self._description is None:
58
+ req = ttypes.TGetResultSetMetadataReq(self._operationHandle)
59
+ response = self._connection.client.GetResultSetMetadata(req)
60
+ hive._check_status(response)
61
+ columns = response.schema.columns
62
+ self._description = []
63
+ # If it's a cypher query, column comment is not null
64
+ self._column_comments = [
65
+ col.comment for col in response.schema.columns
66
+ if col.comment is not None
67
+ ]
68
+
69
+ for col in columns:
70
+ primary_type_entry = col.typeDesc.types[0]
71
+ if primary_type_entry.primitiveEntry is None:
72
+ # All fancy stuff maps to string
73
+ type_code = ttypes.TTypeId._VALUES_TO_NAMES[
74
+ ttypes.TTypeId.STRING_TYPE]
75
+ else:
76
+ type_id = primary_type_entry.primitiveEntry.type
77
+ type_code = ttypes.TTypeId._VALUES_TO_NAMES[type_id]
78
+ self._description.append(
79
+ (col.columnName.decode('utf-8')
80
+ if sys.version_info[0] == 2 else col.columnName,
81
+ type_code.decode('utf-8') if sys.version_info[0] == 2 else
82
+ type_code, None, None, None, None, True))
83
+ return self._description
84
+
85
+ def fetchone(self):
86
+ row = super(CypherResult, self).fetchone()
87
+
88
+ if row is None:
89
+ return None
90
+
91
+ parsed_row = []
92
+ for i in range(0, len(self._column_comments)):
93
+ parsed_row.append(
94
+ self._convertData(self._column_comments[i], row[i]))
95
+
96
+ return tuple(parsed_row)
97
+
98
+ def _convertData(self, type, data):
99
+ """Convert Crux type to Readable type"""
100
+ if type == 'boolean':
101
+ return bool(data)
102
+ elif type == 'int':
103
+ return int(data)
104
+ elif type == 'long':
105
+ return int(data)
106
+ elif type == 'float' or type == 'double':
107
+ return float(data)
108
+ elif type == 'CruxType:Node' or type == 'GraphNode':
109
+ return Vertex.parseVertexFromJson(data)
110
+ elif type == 'CruxType:Relation' or type == 'GraphRelation':
111
+ return Edge.parseEdgeFromJson(self._graph_schema, data)
112
+ elif type == 'CruxType:Path':
113
+ return Path.parsePathFromJson(self._graph_schema, data)
114
+ elif type.startswith('CruxType:List'):
115
+ return self._parseList(type, data)
116
+ elif type.startswith('CruxType:Map'):
117
+ return self._parseMap(type, data)
118
+ else:
119
+ return data
120
+
121
+ def _parseList(self, type, data):
122
+ """Parse 'CruxType:List' type"""
123
+ parsed_data = json.loads(data)
124
+ newType = type[len('CruxType:List') + 1:type.find('>')]
125
+
126
+ return [self._convertData(newType, json.dumps(entry)) for entry in parsed_data]
127
+
128
+ def _parseMap(self, type, data):
129
+ """Parse 'CruxType:Map' type"""
130
+ parsed_data = json.loads(data)
131
+ newTypes = type[len('CruxType:Map') + 1:-2].split(',')
132
+
133
+ result = {}
134
+
135
+ for entry in parsed_data.keys():
136
+ key = self._convertData(newTypes[0], entry)
137
+ result[key] = self._convertData(newTypes[1], parsed_data[entry])
138
+
139
+ return result
140
+
141
+ def toRDD(self, sc, parallelism=1):
142
+ """
143
+ Transform to RDD
144
+ param sc: SparkContext
145
+ param parallelism: RDD parallelism
146
+ """
147
+ return transformToRDD(self, sc, parallelism)
148
+
149
+ class Connection(object):
150
+ """
151
+ Connection class for connecting to the Stellar database.
152
+ """
153
+
154
+ def __init__(
155
+ self,
156
+ host: str,
157
+ port: int,
158
+ auth: Union[str, None] = None,
159
+ username: Union[str, None] = None,
160
+ password: Union[str, None] = None,
161
+ kerberos_service_name: Union[str, None] = None,):
162
+ """Connect to HiveServer2
163
+
164
+ :param host: What host HiveServer2 runs on
165
+ :param port: What port HiveServer2 runs on. Defaults to 10000.
166
+ :param auth: The value of hive.server2.authentication used by HiveServer2. Defaults to ``NONE``.
167
+ :param username: Use with auth='LDAP' only
168
+ :param password: Use with auth='LDAP' only
169
+ :param kerberos_service_name: Use with auth='KERBEROS' only
170
+
171
+ The way to support LDAP and GSSAPI is originated from cloudera/Impyla:
172
+ https://github.com/cloudera/impyla/blob/255b07ed973d47a3395214ed92d35ec0615ebf62
173
+ /impala/_thrift_api.py#L152-L160
174
+ """
175
+ username = username or getpass.getuser()
176
+
177
+ if (password is not None) != (auth in ('LDAP', 'CUSTOM')):
178
+ raise ValueError(
179
+ "Password should be set if and only if in LDAP or CUSTOM mode; "
180
+ "Remove password or use one of those modes")
181
+ if (kerberos_service_name is not None) != (auth == 'KERBEROS'):
182
+ raise ValueError(
183
+ "kerberos_service_name should be set if and only if in KERBEROS mode"
184
+ )
185
+ if port is None:
186
+ port = 10000
187
+ if auth is None:
188
+ auth = 'NONE'
189
+ socket = thrift.transport.TSocket.TSocket(host, port)
190
+ if auth == 'NOSASL':
191
+ # NOSASL corresponds to hive.server2.authentication=NOSASL in hive-site.xml
192
+ self._transport = thrift.transport.TTransport.TBufferedTransport(socket)
193
+ elif auth in ('LDAP', 'KERBEROS', 'NONE', 'CUSTOM'):
194
+ import thrift_sasl
195
+
196
+ if auth == 'KERBEROS':
197
+ # KERBEROS mode in hive.server2.authentication is GSSAPI in SASL
198
+ sasl_auth = 'GSSAPI'
199
+ else:
200
+ sasl_auth = 'PLAIN'
201
+ if password is None:
202
+ # Password doesn't matter in NONE mode, just needs to be nonempty.
203
+ password = 'x'
204
+
205
+ def sasl_factory():
206
+ if sasl_auth == 'GSSAPI':
207
+ sasl_client = PureSASLClient(host, mechanism="GSSAPI", service=kerberos_service_name)
208
+ elif sasl_auth == 'PLAIN':
209
+ sasl_client = PureSASLClient(host, mechanism="PLAIN", username=username, password=password)
210
+ else:
211
+ raise AssertionError("Unsupported SASL mechanism")
212
+ return sasl_client
213
+
214
+ self._transport = thrift_sasl.TSaslClientTransport(sasl_factory, sasl_auth, socket)
215
+ else:
216
+ # All HS2 config options:
217
+ # https://cwiki.apache.org/confluence/display/Hive/Setting+Up+HiveServer2#SettingUpHiveServer2-Configuration
218
+ # PAM currently left to end user via thrift_transport option.
219
+ raise NotImplementedError(
220
+ "Only NONE, NOSASL, LDAP, KERBEROS, CUSTOM "
221
+ "authentication are supported, got {}".format(auth))
222
+ protocol = thrift.protocol.TBinaryProtocol.TBinaryProtocol(self._transport)
223
+ self._client = TCLIService.Client(protocol)
224
+ # oldest version that still contains features we care about
225
+ # "V6 uses binary type for binary payload (was string) and uses columnar result set"
226
+ protocol_version = ttypes.TProtocolVersion.HIVE_CLI_SERVICE_PROTOCOL_V6
227
+ try:
228
+ self._transport.open()
229
+ open_session_req = ttypes.TOpenSessionReq(
230
+ client_protocol=protocol_version,
231
+ configuration={},
232
+ username=username,
233
+ )
234
+ response = self._client.OpenSession(open_session_req)
235
+ hive._check_status(response)
236
+ assert response.sessionHandle is not None, "Expected a session from OpenSession"
237
+ self._sessionHandle = response.sessionHandle
238
+ assert response.serverProtocolVersion == protocol_version, \
239
+ "Unable to handle protocol version {}".format(response.serverProtocolVersion)
240
+
241
+ except:
242
+ self._transport.close()
243
+ raise
244
+
245
+ def __enter__(self):
246
+ """Transport should already be opened by __init__"""
247
+ return self
248
+
249
+ def __exit__(self, exc_type, exc_value, traceback):
250
+ """Call close"""
251
+ self.close()
252
+
253
+ @property
254
+ def client(self):
255
+ return self._client
256
+
257
+ @property
258
+ def sessionHandle(self):
259
+ return self._sessionHandle
260
+
261
+ def close(self):
262
+ req = ttypes.TCloseSessionReq(sessionHandle=self._sessionHandle)
263
+ response = self._client.CloseSession(req)
264
+ self._transport.close()
265
+ hive._check_status(response)
266
+
267
+ def execute(self, operation: str, graph_schema: Union[GraphSchema, None] = None) -> CypherResult:
268
+ """
269
+ Execute a query on the database.
270
+
271
+ :param operation: The query to execute
272
+ """
273
+ cursor = CypherResult(self, graph_schema)
274
+ cursor.execute(operation)
275
+ return cursor
276
+
@@ -0,0 +1,110 @@
1
+ """
2
+ Define database class for operations on a single graph.
3
+ """
4
+
5
+ from __future__ import absolute_import
6
+ from types import TracebackType
7
+ import logging
8
+ import json
9
+ from typing import Any, Union
10
+
11
+ from ..graph_types import GraphSchema
12
+ from .connection import Connection, CypherResult
13
+
14
+ _logger = logging.getLogger(__name__)
15
+
16
+ class Graph(object):
17
+ """
18
+ Database class for operations on a single graph.
19
+ """
20
+
21
+ def __init__(self, graph_name: str, connection: Connection):
22
+ self.graph_name = graph_name
23
+ self.connection = connection
24
+ self.graph_schema: GraphSchema = None
25
+ self._init_connection()
26
+
27
+ def __enter__(self):
28
+ """
29
+ Enter the database context.
30
+ """
31
+ return self
32
+
33
+ def __exit__(self, exc_type, exc_value, traceback):
34
+ """
35
+ Exit the database context.
36
+ """
37
+ if self.connection:
38
+ self.connection.close()
39
+ self.connection = None
40
+
41
+ def _init_connection(self):
42
+ """
43
+ Initialize the connection to the database.
44
+ """
45
+
46
+ self.connection.execute('config query.lang cypher')
47
+
48
+ """Try to bind cursor with graph name."""
49
+ try:
50
+ self.connection.execute(f'use graph {self.graph_name}')
51
+ self._get_graph_schema()
52
+ except Exception as e:
53
+ _logger.debug(f"graph {self.graph_name} not found")
54
+
55
+ def _get_graph_schema(self):
56
+ """
57
+ Get the graph schema.
58
+ """
59
+ query_result = self.connection.execute('DESCRIBE GRAPH {} RAW'.format(self.graph_name))
60
+ schemaInJson = query_result.fetchone()[0]
61
+ query_result.close()
62
+ self.graph_schema = GraphSchema.parseSchemaFromJson(schemaInJson)
63
+
64
+ # get schema from data
65
+ try:
66
+ query_result = self.connection.execute('manipulate graph {} get_schema_from_data'.format(self.graph_name))
67
+ self.graph_labels = query_result.fetchone()[0]
68
+ query_result.close()
69
+ self.graph_labels = json.loads(self.graph_labels)
70
+ except:
71
+ pass
72
+
73
+ def execute(self, query: str, *args, **kwargs) -> CypherResult:
74
+ """
75
+ Execute a query on the database.
76
+ """
77
+ query_result = self.connection.execute(query, self.graph_schema)
78
+
79
+ if (query.strip().lower().startswith('create graph')):
80
+ self._get_graph_schema()
81
+
82
+ return query_result
83
+
84
+ def create(self, cypher: str):
85
+ """
86
+ Create a graph.
87
+ """
88
+ create_result = self.connection.execute(cypher)
89
+ create_result.close()
90
+ self._get_graph_schema()
91
+
92
+ def drop(self):
93
+ """
94
+ Drop the graph.
95
+ """
96
+ drop_result = self.connection.execute('DROP GRAPH {}'.format(self.graph_name))
97
+ drop_result.close()
98
+ self.graph_schema = None
99
+
100
+ def get_graph_schema(self) -> Union[GraphSchema, None]:
101
+ """
102
+ Get the graph schema.
103
+ """
104
+ return self.graph_schema
105
+
106
+ def get_labels(self) -> Any:
107
+ """
108
+ Get the labels of the graph.
109
+ """
110
+ return self.graph_labels
@@ -1,10 +0,0 @@
1
- pystellardb/__init__.py,sha256=JOl41NviMN-qDV0Z8ZPmhNIxvgyauGGJHdB4A-8MhqM,93
2
- pystellardb/_version.py,sha256=cwBvUjWVT20dAp04I_QQFlk81VsfhUWKUfT2FKtmxhk,498
3
- pystellardb/graph_types.py,sha256=XbtPebhaRV7OimW-GxFJGSOcp-OnzWrlIe9M7Xv3sbU,14032
4
- pystellardb/stellar_hive.py,sha256=xVnONjG03CLNwW3dymR2ZqXiEUvcMlIvPIOxzyoDFfE,15067
5
- pystellardb/stellar_rdd.py,sha256=TYwsWYeCxfOliGq1kV3ArNXdye55cKWZF7s9M9nDdt4,1324
6
- PyStellarDB-0.13.5.dist-info/LICENSE,sha256=1qDFxrywejs7xNBfOr6T-7lOuqDgSNIES77kTYege3w,560
7
- PyStellarDB-0.13.5.dist-info/METADATA,sha256=Lg8U6us8NJ-hlFQEwaZDsfGZOgvPKnChM7lY6HtTt3s,9390
8
- PyStellarDB-0.13.5.dist-info/WHEEL,sha256=_4XEmVmaBFWtekSGrbfOGNjC2I5lUr0lZSRblBllIFA,109
9
- PyStellarDB-0.13.5.dist-info/top_level.txt,sha256=DRk-SeGVCdVAzv2CwFmdu75Yo7DgjUA3Hpu-9l8qPuU,12
10
- PyStellarDB-0.13.5.dist-info/RECORD,,