Skip to content

Commit c4a8c72

Browse files
Merge branch 'release-v5.5.2'
2 parents db90048 + 209dd38 commit c4a8c72

14 files changed

Lines changed: 315 additions & 79 deletions

File tree

.travis.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ language: python
33
dist: xenial
44

55
python:
6-
- '3.4'
7-
- '3.5'
86
- '3.6'
97
- '3.7'
108

btrdb/conn.py

Lines changed: 89 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,46 @@
1-
# Copyright (c) 2017 Sam Kumar <samkumar@berkeley.edu>
2-
# Copyright (c) 2017 Michael P Andersen <m.andersen@cs.berkeley.edu>
3-
# Copyright (c) 2017 University of California, Berkeley
4-
# All rights reserved.
1+
# btrdb.conn
2+
# Connection related objects for the BTrDB library
53
#
6-
# Redistribution and use in source and binary forms, with or without
7-
# modification, are permitted provided that the following conditions are met:
8-
# * Redistributions of source code must retain the above copyright
9-
# notice, this list of conditions and the following disclaimer.
10-
# * Redistributions in binary form must reproduce the above copyright
11-
# notice, this list of conditions and the following disclaimer in the
12-
# documentation and/or other materials provided with the distribution.
13-
# * Neither the name of the University of California, Berkeley nor the
14-
# names of its contributors may be used to endorse or promote products
15-
# derived from this software without specific prior written permission.
4+
# Author: PingThings
5+
# Created: Fri Dec 21 14:57:30 2018 -0500
166
#
17-
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18-
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19-
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20-
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE LIABLE FOR
21-
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22-
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23-
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24-
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25-
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26-
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
7+
# For license information, see LICENSE.txt
8+
# ID: conn.py [] allen@pingthings.io $
279

2810
"""
29-
The 'btrdb' module provides Python bindings to interact with BTrDB.
11+
Connection related objects for the BTrDB library
3012
"""
3113

32-
import grpc
33-
import uuid as uuidlib
14+
##########################################################################
15+
## Imports
16+
##########################################################################
17+
3418
import os
19+
import re
20+
import json
21+
import uuid as uuidlib
3522

23+
import grpc
3624
from grpc._cython.cygrpc import CompressionAlgorithm
3725

3826
from btrdb.stream import Stream, StreamSet
3927
from btrdb.utils.general import unpack_stream_descriptor
4028
from btrdb.utils.conversion import to_uuid
29+
from btrdb.exceptions import NotFound
30+
31+
##########################################################################
32+
## Module Variables
33+
##########################################################################
4134

4235
MIN_TIME = -(16 << 56)
4336
MAX_TIME = 48 << 56
4437
MAX_POINTWIDTH = 63
4538

4639

40+
##########################################################################
41+
## Classes
42+
##########################################################################
43+
4744
class Connection(object):
4845

4946
def __init__(self, addrportstr, apikey=None):
@@ -106,6 +103,43 @@ class BTrDB(object):
106103
def __init__(self, endpoint):
107104
self.ep = endpoint
108105

106+
def query(self, stmt, params=[]):
107+
"""
108+
Performs a SQL query on the database metadata and returns a list of
109+
dictionaries from the resulting cursor.
110+
111+
Parameters
112+
----------
113+
stmt: str
114+
a SQL statement to be executed on the BTrDB metadata. Available
115+
tables are noted below. To sanitize inputs use a `$1` style parameter such as
116+
`select * from streams where name = $1 or name = $2`.
117+
params: list or tuple
118+
a list of parameter values to be sanitized and interpolated into the
119+
SQL statement. Using parameters forces value/type checking and is
120+
considered a best practice at the very least.
121+
122+
Returns
123+
-------
124+
list
125+
a list of dictionary object representing the cursor results.
126+
127+
128+
Notes
129+
-------
130+
Parameters will be inserted into the SQL statement as noted by the
131+
paramter number such as `$1`, `$2`, or `$3`. The `streams` table is
132+
available for `SELECT` statements only.
133+
134+
See https://btrdb.readthedocs.io/en/latest/ for more info.
135+
"""
136+
return [
137+
json.loads(row.decode("utf-8"))
138+
for page in self.ep.sql_query(stmt, params)
139+
for row in page
140+
]
141+
142+
109143
def streams(self, *identifiers, versions=None):
110144
"""
111145
Returns a StreamSet object with BTrDB streams from the supplied
@@ -129,7 +163,31 @@ def streams(self, *identifiers, versions=None):
129163
if versions and len(versions) != len(identifiers):
130164
raise ValueError("number of versions does not match identifiers")
131165

132-
streams = [self.stream_from_uuid(ident) for ident in identifiers]
166+
streams = []
167+
for ident in identifiers:
168+
if isinstance(ident, uuidlib.UUID):
169+
streams.append(self.stream_from_uuid(ident))
170+
continue
171+
172+
if isinstance(ident, str):
173+
# attempt UUID lookup
174+
pattern = "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"
175+
if re.match(pattern, ident):
176+
streams.append(self.stream_from_uuid(ident))
177+
continue
178+
179+
# attempt collection/name lookup
180+
if "/" in ident:
181+
parts = ident.split("/")
182+
found = self.streams_in_collection("/".join(parts[:-1]), tags={"name": parts[-1]})
183+
if len(found) == 1:
184+
streams.append(found[0])
185+
continue
186+
raise NotFound(f"Could not identify stream `{ident}`")
187+
188+
raise ValueError(f"Could not identify stream based on `{ident}`. Identifier must be UUID or collection/name.")
189+
190+
133191
obj = StreamSet(streams)
134192

135193
if versions:
@@ -249,6 +307,9 @@ def streams_in_collection(self, *collection, is_collection_prefix=True, tags=Non
249307
if annotations is None:
250308
annotations = {}
251309

310+
if not collection:
311+
collection = [None]
312+
252313
for item in collection:
253314
streams = self.ep.lookupStreams(item, is_collection_prefix, tags, annotations)
254315
for desclist in streams:

btrdb/endpoint.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,3 +212,9 @@ def generateCSV(self, queryType, start, end, width, depth, includeVersions, *str
212212
for result in self.stub.GenerateCSV(params):
213213
BTrDBError.checkProtoStat(result.stat)
214214
yield result.row
215+
216+
def sql_query(self, stmt, params=[]):
217+
request = btrdb_pb2.SQLQueryParams(query=stmt, params=params)
218+
for page in self.stub.SQLQuery(request):
219+
BTrDBError.checkProtoStat(page.stat)
220+
yield page.SQLQueryRow

btrdb/stream.py

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -89,9 +89,14 @@ def refresh_metadata(self):
8989
self._known_to_exist = True
9090

9191
# deserialize annoation values
92-
self._annotations = dict(
93-
[[k, json.loads(v)] for k, v in self._annotations.items()]
94-
)
92+
parts = []
93+
for k, v in self._annotations.items():
94+
try:
95+
parts.append([k, json.loads(v)])
96+
except json.decoder.JSONDecodeError:
97+
parts.append([k, v])
98+
99+
self._annotations = dict(parts)
95100

96101
def exists(self):
97102
"""
@@ -174,6 +179,21 @@ def name(self):
174179
"""
175180
return self.tags()["name"]
176181

182+
@property
183+
def unit(self):
184+
"""
185+
Returns the stream's unit which is parsed from the stream tags. This
186+
may require a round trip to the server depending on how the stream was
187+
acquired.
188+
189+
Returns
190+
-------
191+
str
192+
The unit for values of the stream.
193+
194+
"""
195+
return self.tags()["unit"]
196+
177197
@property
178198
def collection(self):
179199
"""
@@ -921,7 +941,7 @@ def filter(self, start=None, end=None, collection=None, name=None, unit=None,
921941
# filters if the subset of the annotations matches the given annotations
922942
obj._streams = [
923943
s for s in obj._streams
924-
if annotations.items() <= s.annotations().items()
944+
if annotations.items() <= s.annotations()[0].items()
925945
]
926946

927947
return obj

btrdb/utils/credentials.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,9 @@ def credentials(endpoints=None, apikey=None):
132132
"""
133133
creds = {}
134134
credentials_by_arg = filter_none(lambda: { "endpoints": endpoints, "apikey": apikey, })
135-
pipeline = (credentials_by_profile, credentials_by_env, credentials_by_arg)
135+
pipeline = [credentials_by_env, credentials_by_arg]
136+
if os.path.exists(CREDENTIALS_PATH):
137+
pipeline.insert(0, credentials_by_profile)
138+
136139
[creds.update(func()) for func in pipeline]
137140
return creds
138-

btrdb/version.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@
1818
__version_info__ = {
1919
'major': 5,
2020
'minor': 5,
21-
'micro': 1,
21+
'micro': 2,
2222
'releaselevel': 'final',
23-
'serial': 9,
23+
'serial': 10,
2424
}
2525

2626
##########################################################################

docs/source/conf.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
'sphinx.ext.napoleon',
5050
'sphinx.ext.todo',
5151
'sphinx.ext.githubpages',
52+
'sphinx.ext.intersphinx',
5253
]
5354

5455
# Add any paths that contain templates here, relative to this directory.

docs/source/maintainers/anaconda.rst

Lines changed: 1 addition & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ Anaconda Package Creation Walkthrough
33

44
This walkthrough documents the process to create and upload the btrdb package to Anaconda Cloud. At a high level, this will guide you through the following steps:
55

6-
1. Activate your local environment and install dependenciess
6+
1. Activate your local environment and install dependencies
77
2. Create and edit the `meta.yaml` file
88
3. Create local system builds for 3.6 and 3.7
99
4. Create builds for other platforms using conda's conversion command
@@ -117,27 +117,6 @@ This will create a `btrdb` subfolder containing the `meta.yaml` file we need. A
117117
$ cd ../
118118
$ rm -rf conda.recipe/
119119
120-
Modify the requirements file
121-
-----------------------------
122-
123-
At this time, the codebase has pinned the grpc dependencies at `1.12.1` which needs to be modified by hand as that version doesnt exist in Anaconda. A diff is provided below so you can edit or patch the file.
124-
125-
.. code-block:: diff
126-
127-
diff --git a/requirements.txt b/requirements.txt
128-
index f504f26..403b1d4 100644
129-
--- a/requirements.txt
130-
+++ b/requirements.txt
131-
@@ -1,6 +1,6 @@
132-
# GRPC / Protobuff related
133-
-grpcio==1.12.1
134-
-grpcio-tools==1.12.1
135-
+grpcio==1.16.1
136-
+grpcio-tools==1.16.1
137-
138-
# Time related utils
139-
pytz
140-
141120
142121
Modify the meta.yaml file
143122
--------------------------
@@ -169,19 +148,11 @@ You may wish to update the build number as well. A patch is supplied below to g
169148
170149
requirements:
171150
host:
172-
- - grpcio ==1.12.1
173-
- - grpcio-tools ==1.12.1
174-
+ - grpcio ==1.16.1
175-
+ - grpcio-tools ==1.16.1
176151
- pip
177152
- python
178153
- pytz
179154
+ - pytest-runner
180155
run:
181-
- - grpcio ==1.12.1
182-
- - grpcio-tools ==1.12.1
183-
+ - grpcio ==1.16.1
184-
+ - grpcio-tools ==1.16.1
185156
- python
186157
- pytz
187158
+ - pytest-runner

docs/source/working/stream-query-manage.rst

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ supplied UUID cannot be found then :code:`None` will be returned.
4444
stream = conn.stream_from_uuid("71466a91-dcfe-42ea-9e88-87c51f847942")
4545
4646
47-
Query by collection
47+
Finding by collection
4848
--------------------------
4949
You can also search for multiple streams by collection using the server object's
5050
:code:`streams_in_collection` method which will return a simple list of
@@ -56,3 +56,44 @@ detail.
5656
5757
conn = btrdb.connect()
5858
streams = conn.streams_in_collection("NORTHEAST/NH")
59+
60+
61+
Querying Metadata
62+
-----------------
63+
Finally, you can query for metadata using standard SQL although at the moment, only the
64+
`streams` table is available. SQL queries can be submitted using the `query`
65+
method which accepts both a `stmt` and `params` argument. The `stmt` should
66+
contain the SQL you'd like executed with parameter placeholders such as `$1` or
67+
`$2` as shown below.
68+
69+
.. code-block:: python
70+
71+
conn = btrdb.connect()
72+
stmt = "select uuid from streams where name = $1 or name = $2"
73+
params = ["Boston_1", "Boston_2"]
74+
75+
for row in conn.query(stmt, params):
76+
print(row)
77+
78+
The SQL query results are returned as a list of dictionaries where each key
79+
matches a column from your SQL projection. You can choose your columns from the
80+
schema of the streams table as follows.
81+
82+
83+
+------------------+------------------------+-----------+
84+
| Column | Type | Nullable |
85+
+==================+========================+===========+
86+
| uuid | uuid | not null |
87+
+------------------+------------------------+-----------+
88+
| collection | character varying(256) | not null |
89+
+------------------+------------------------+-----------+
90+
| name | character varying(256) | not null |
91+
+------------------+------------------------+-----------+
92+
| unit | character varying(256) | not null |
93+
+------------------+------------------------+-----------+
94+
| ingress | character varying(256) | not null |
95+
+------------------+------------------------+-----------+
96+
| property_version | bigint | not null |
97+
+------------------+------------------------+-----------+
98+
| annotations | hstore | |
99+
+------------------+------------------------+-----------+

setup.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,6 @@
5252
'Natural Language :: English',
5353
'Operating System :: OS Independent',
5454
'Programming Language :: Python',
55-
'Programming Language :: Python :: 3.4',
56-
'Programming Language :: Python :: 3.5',
5755
'Programming Language :: Python :: 3.6',
5856
'Programming Language :: Python :: 3.7',
5957
'Topic :: Database',
@@ -153,7 +151,7 @@ def get_description_type(path=PKG_DESCRIBE):
153151
"console_scripts": [],
154152
},
155153
"install_requires": list(get_requires()),
156-
"python_requires": ">=3.4, <4",
154+
"python_requires": ">=3.6, <4",
157155
"setup_requires":["pytest-runner"],
158156
"tests_require":["pytest"],
159157
}

0 commit comments

Comments
 (0)