def test_ref_std_method(): if six.PY2: assert str(hszinc.Ref( name='a.ref', value='display text')) == '@a.ref u\'display text\'' else: assert str(hszinc.Ref( name='a.ref', value='display text')) == '@a.ref \'display text\''
def test_data_types_v3(): grid = hszinc.Grid(version=hszinc.VER_3_0) grid.column['comment'] = {} grid.column['value'] = {} grid.extend([ { 'comment': 'A NA', 'value': hszinc.NA, }, { 'comment': 'An empty list', 'value': [], }, { 'comment': 'A null value in a list', 'value': [None], }, { 'comment': 'A marker in a list', 'value': [hszinc.MARKER], }, { 'comment': 'Booleans', 'value': [True, False], }, { 'comment': 'References', 'value': [hszinc.Ref('a-ref'), hszinc.Ref('a-ref', 'a value')], }, { 'comment': 'A quantity', 'value': [hszinc.Quantity(500, 'miles')], }, { 'comment': 'A XStr', 'value': [hszinc.XStr("hex", 'deadbeef')], }, ]) grid_str = hszinc.dump(grid) ref_str = '''ver:"3.0" comment,value "A NA",NA "An empty list",[] "A null value in a list",[N] "A marker in a list",[M] "Booleans",[T,F] "References",[@a-ref,@a-ref "a value"] "A quantity",[500miles] "A XStr",[hex("deadbeef")] ''' assert grid_str == ref_str
def get_histories(point_id, rng): """ """ # getting history as dataframe history_op = session.his_read_series(hszinc.Ref(point_id), rng=rng) history_op.wait() history = history_op.result return history
def _preprocess_entity(e): if not isinstance(e, dict): raise TypeError('%r is not a dict' % e) e = e.copy() e_id = e.pop('id') if isinstance(e_id, hszinc.Ref): e_id = e_id.name if '.' in e_id: e_id = e_id.split('.')[-1] e['id'] = hszinc.Ref(e_id) return e
def gen_random_ref(): # Generate a randomised reference. name = gen_random_str(charset=\ string.ascii_letters + string.digits\ + '_:-.~') if random.choice([True, False]): value = gen_random_str() else: value = None return hszinc.Ref(name, value)
def _refresh_meta(self, force=False): """ Retrieve the metadata from the server if out of date. """ if not (force or self._refresh_due): # We're not being forced and the refresh isn't due yet. return meta = self._session._get_grid('read', \ id=hszinc.dump_scalar(hszinc.Ref(self._point_id))) self._load_meta(meta[0])
def _preprocess_entity(e): if not isinstance(e, dict): raise TypeError("%r is not a dict" % e) e = e.copy() if "id" in e: e_id = e.pop("id") if isinstance(e_id, hszinc.Ref): e_id = e_id.name if "." in e_id: e_id = e_id.split(".")[-1] e["id"] = hszinc.Ref(e_id) return e
def test_read_one_id(self, server_session): (server, session) = server_session op = session.read(hszinc.Ref("my.entity.id")) # The operation should still be in progress assert not op.is_done # There shall be one request assert server.requests() == 1 rq = server.next_request() # Request shall be a GET assert rq.method == "GET", "Expecting GET, got %s" % rq # Request shall be for a specific URI assert rq.uri == BASE_URI + "api/read?id=%40my.entity.id" # Accept header shall be given assert rq.headers[b"Accept"] == "text/zinc" # Make a grid to respond with expected = hszinc.Grid() expected.column["id"] = {} expected.column["dis"] = {} expected.extend([{ "id": hszinc.Ref("my.entity.id"), "dis": "my entity" }]) rq.respond( status=200, headers={b"Content-Type": "text/zinc"}, content=hszinc.dump(expected, mode=hszinc.MODE_ZINC), ) # State machine should now be done assert op.is_done actual = op.result grid_cmp(expected, actual)
def test_read_one_id(self, server_session): (server, session) = server_session op = session.read(hszinc.Ref('my.entity.id')) # The operation should still be in progress assert not op.is_done # There shall be one request assert server.requests() == 1 rq = server.next_request() # Request shall be a GET assert rq.method == 'GET', 'Expecting GET, got %s' % rq # Request shall be for a specific URI assert rq.uri == BASE_URI + 'api/read?id=%40my.entity.id' # Accept header shall be given assert rq.headers[b'Accept'] == 'text/zinc' # Make a grid to respond with expected = hszinc.Grid() expected.column['id'] = {} expected.column['dis'] = {} expected.extend([{ "id": hszinc.Ref('my.entity.id'), "dis": 'my entity' }]) rq.respond(status=200, headers={ b'Content-Type': 'text/zinc', }, content=hszinc.dump(expected, mode=hszinc.MODE_ZINC)) # State machine should now be done assert op.is_done actual = op.result grid_cmp(expected, actual)
def _obj_to_ref(self, obj): """ Convert an arbitrary object referring to an entity to an entity reference. """ if isinstance(obj, hszinc.Ref): return obj if isinstance(obj, string_types): return hszinc.Ref(obj) if hasattr(obj, "id"): return obj.id raise NotImplementedError("Don't know how to get the ID from a %s" % obj.__class__.__name__)
def test_scalar_dict_zinc_v3(): grid = hszinc.Grid(version=hszinc.VER_3_0) grid.column['comment'] = {} grid.column['value'] = {} grid.extend([ { 'comment': 'An empty dict', 'value': {}, }, { 'comment': 'A marker in a dict', 'value': { "marker": hszinc.MARKER }, }, { 'comment': 'A references in a dict', 'value': { "ref": hszinc.Ref('a-ref'), "ref2": hszinc.Ref('a-ref', 'a value') }, }, { 'comment': 'A quantity in a dict', 'value': { "quantity": hszinc.Quantity(500, 'miles') }, }, ]) grid_str = hszinc.dump(grid, mode=hszinc.MODE_ZINC) assert grid_str == ("ver:\"3.0\"\n" "comment,value\n" "\"An empty dict\",{}\n" "\"A marker in a dict\",{marker:M}\n" "\"A references in a dict\",{" + " ".join([str(k) + ":" + str(v) for k, v in {"ref": "@a-ref", "ref2": "@a-ref"}.items()]) \ .replace("ref2:@a-ref", "ref2:@a-ref \"a value\"") + \ "}\n" "\"A quantity in a dict\",{quantity:500miles}\n")
def test_get_single_entity(self, server_session): (server, session) = server_session # Try retrieving an existing single entity op = session.get_entity("my.entity.id", single=True) # The operation should still be in progress assert not op.is_done # There shall be one request assert server.requests() == 1 rq = server.next_request() # Request shall be a GET assert rq.method == "GET", "Expecting GET, got %s" % rq # Request shall be for base + 'api/[email protected]' assert rq.uri == BASE_URI + "api/read?id=%40my.entity.id" # Accept header shall be given assert rq.headers[b"Accept"] == "text/zinc" # Make a grid to respond with response = hszinc.Grid() response.column["id"] = {} response.column["dis"] = {} response.column["randomTag"] = {} response.append({ "id": hszinc.Ref("my.entity.id", value="id"), "dis": "A test entity", "randomTag": hszinc.MARKER, }) rq.respond( status=200, headers={b"Content-Type": "text/zinc"}, content=hszinc.dump(response, mode=hszinc.MODE_ZINC), ) # State machine should now be done assert op.is_done entity = op.result # Response should be an entity assert isinstance(entity, Entity), "%r not an entity" % entity # The tags should be passed through from the response assert entity.id.name == "my.entity.id" assert entity.tags["dis"] == response[0]["dis"] assert entity.tags["randomTag"] == response[0]["randomTag"]
def test_get_single_entity(self, server_session): (server, session) = server_session # Try retrieving an existing single entity op = session.get_entity('my.entity.id', single=True) # The operation should still be in progress assert not op.is_done # There shall be one request assert server.requests() == 1 rq = server.next_request() # Request shall be a GET assert rq.method == 'GET', 'Expecting GET, got %s' % rq # Request shall be for base + 'api/[email protected]' assert rq.uri == BASE_URI + 'api/read?id=%40my.entity.id' # Accept header shall be given assert rq.headers[b'Accept'] == 'text/zinc' # Make a grid to respond with response = hszinc.Grid() response.column['id'] = {} response.column['dis'] = {} response.column['randomTag'] = {} response.append({ 'id': hszinc.Ref('my.entity.id', value='id'), 'dis': 'A test entity', 'randomTag': hszinc.MARKER }) rq.respond(status=200, headers={ b'Content-Type': 'text/zinc', }, content=hszinc.dump(response, mode=hszinc.MODE_ZINC)) # State machine should now be done assert op.is_done entity = op.result # Response should be an entity assert isinstance(entity, Entity), '%r not an entity' % entity # The tags should be passed through from the response assert entity.id.name == 'my.entity.id' assert entity.tags['dis'] == response[0]['dis'] assert entity.tags['randomTag'] == response[0]['randomTag']
def his_write(self, records): """ Write historical data to this data point. records should be a list of dicts with the following keys: - ts: a datetime.datetime object with the timestamp of the reading. - val: the value to be written at that time. """ if not self.has_meta('his'): raise NotImplementedError('%s does not implement history' \ % self._point_id) if isinstance(records, dict): records = [records] grid = hszinc.Grid() grid.metadata['id'] = hszinc.Ref(self._point_id) grid.column['ts'] = {} grid.column['val'] = {} grid.extend([ {'ts': self._localise_dt(r['ts']), 'val': r['val']} for r in records ]) self._session._post_grid('hisWrite', grid)
def test_read_many_id(self, server_session): (server, session) = server_session op = session.read([ hszinc.Ref('my.entity.id1'), hszinc.Ref('my.entity.id2'), hszinc.Ref('my.entity.id3'), ]) # The operation should still be in progress assert not op.is_done # There shall be one request assert server.requests() == 1 rq = server.next_request() # Request shall be a GET assert rq.method == 'POST', 'Expecting POST, got %s' % rq # Request shall be for a specific URI assert rq.uri == BASE_URI + 'api/read' # Body shall be in ZINC assert rq.headers['Content-Type'] == 'text/zinc' # Body shall be a single valid grid of this form: expected = hszinc.Grid() expected.column['id'] = {} expected.extend([{ "id": hszinc.Ref('my.entity.id1'), }, { "id": hszinc.Ref('my.entity.id2'), }, { "id": hszinc.Ref('my.entity.id3'), }]) actual = hszinc.parse(rq.body, mode=hszinc.MODE_ZINC) assert len(actual) == 1 grid_cmp(expected, actual[0]) # Accept header shall be given assert rq.headers['Accept'] == 'text/zinc' # Make a grid to respond with expected = hszinc.Grid() expected.column['id'] = {} expected.column['dis'] = {} expected.extend([{ "id": hszinc.Ref('my.entity.id1'), "dis": 'my entity 1' }, { "id": hszinc.Ref('my.entity.id2'), "dis": 'my entity 2' }, { "id": hszinc.Ref('my.entity.id3'), "dis": 'my entity 3' }]) rq.respond(status=200, headers={ 'Content-Type': 'text/zinc', }, content=hszinc.dump(expected, mode=hszinc.MODE_ZINC)) # State machine should now be done assert op.is_done actual = op.result grid_cmp(expected, actual)
def test_data_types_json(): grid = hszinc.Grid(version=hszinc.VER_2_0) grid.column['comment'] = {} grid.column['value'] = {} grid.extend([ { 'comment': 'A null value', 'value': None, }, { 'comment': 'A marker', 'value': hszinc.MARKER, }, { 'comment': 'A boolean, indicating False', 'value': False, }, { 'comment': 'A boolean, indicating True', 'value': True, }, { 'comment': 'A reference, without value', 'value': hszinc.Ref('a-ref'), }, { 'comment': 'A reference, with value', 'value': hszinc.Ref('a-ref', 'a value'), }, { 'comment': 'A binary blob', 'value': hszinc.Bin('text/plain'), }, { 'comment': 'A quantity', 'value': hszinc.Quantity(500,'miles'), }, { 'comment': 'A quantity without unit', 'value': hszinc.Quantity(500,None), }, { 'comment': 'A coordinate', 'value': hszinc.Coordinate(-27.4725,153.003), }, { 'comment': 'A URI', 'value': hszinc.Uri('http://www.example.com'), }, { 'comment': 'A string', 'value': 'This is a test\n'\ 'Line two of test\n'\ '\tIndented with "quotes" and \\backslashes\\', }, { 'comment': 'A date', 'value': datetime.date(2016,1,13), }, { 'comment': 'A time', 'value': datetime.time(7,51,43,microsecond=12345), }, { 'comment': 'A timestamp (non-UTC)', 'value': pytz.timezone('Europe/Berlin').localize(\ datetime.datetime(2016,1,13,7,51,42,12345)), }, { 'comment': 'A timestamp (UTC)', 'value': pytz.timezone('UTC').localize(\ datetime.datetime(2016,1,13,7,51,42,12345)), }, ]) grid_json = json.loads(hszinc.dump(grid, mode=hszinc.MODE_JSON)) assert grid_json == { 'meta': {'ver':'2.0'}, 'cols': [ {'name':'comment'}, {'name':'value'}, ], 'rows': [ { 'comment': 's:A null value', 'value': None}, { 'comment': 's:A marker', 'value': 'm:'}, { 'comment': 's:A boolean, indicating False', 'value': False}, { 'comment': 's:A boolean, indicating True', 'value': True}, { 'comment': 's:A reference, without value', 'value': 'r:a-ref'}, { 'comment': 's:A reference, with value', 'value': 'r:a-ref a value'}, { 'comment': 's:A binary blob', 'value': 'b:text/plain'}, { 'comment': 's:A quantity', 'value': 'n:500.000000 miles'}, { 'comment': 's:A quantity without unit', 'value': 'n:500.000000'}, { 'comment': 's:A coordinate', 'value': 'c:-27.472500,153.003000'}, { 'comment': 's:A URI', 'value': 'u:http://www.example.com'}, { 'comment': 's:A string', 'value': 's:This is a test\n'\ 'Line two of test\n'\ '\tIndented with \"quotes\" '\ 'and \\backslashes\\'}, { 'comment': 's:A date', 'value': 'd:2016-01-13'}, { 'comment': 's:A time', 'value': 'h:07:51:43.012345'}, { 'comment': 's:A timestamp (non-UTC)', 'value': 't:2016-01-13T07:51:42.012345+01:00 Berlin'}, { 'comment': 's:A timestamp (UTC)', 'value': 't:2016-01-13T07:51:42.012345+00:00 UTC'}, ], }
def test_read_many_id(self, server_session): (server, session) = server_session op = session.read([ hszinc.Ref("my.entity.id1"), hszinc.Ref("my.entity.id2"), hszinc.Ref("my.entity.id3"), ]) # The operation should still be in progress assert not op.is_done # There shall be one request assert server.requests() == 1 rq = server.next_request() # Request shall be a GET assert rq.method == "POST", "Expecting POST, got %s" % rq # Request shall be for a specific URI assert rq.uri == BASE_URI + "api/read" # Body shall be in ZINC assert rq.headers[b"Content-Type"] == "text/zinc" # Body shall be a single valid grid of this form: expected = hszinc.Grid() expected.column["id"] = {} expected.extend([ { "id": hszinc.Ref("my.entity.id1") }, { "id": hszinc.Ref("my.entity.id2") }, { "id": hszinc.Ref("my.entity.id3") }, ]) actual = hszinc.parse(rq.body.decode("utf-8"), mode=hszinc.MODE_ZINC) assert len(actual) == 1 grid_cmp(expected, actual[0]) # Accept header shall be given assert rq.headers[b"Accept"] == "text/zinc" # Make a grid to respond with expected = hszinc.Grid() expected.column["id"] = {} expected.column["dis"] = {} expected.extend([ { "id": hszinc.Ref("my.entity.id1"), "dis": "my entity 1" }, { "id": hszinc.Ref("my.entity.id2"), "dis": "my entity 2" }, { "id": hszinc.Ref("my.entity.id3"), "dis": "my entity 3" }, ]) rq.respond( status=200, headers={b"Content-Type": "text/zinc"}, content=hszinc.dump(expected, mode=hszinc.MODE_ZINC), ) # State machine should now be done assert op.is_done actual = op.result grid_cmp(expected, actual)
def test_get_multi_entity(self, server_session): (server, session) = server_session # Try retrieving existing multiple entities op = session.get_entity(["my.entity.id1", "my.entity.id2"], single=False) # The operation should still be in progress assert not op.is_done # There shall be one request assert server.requests() == 1 rq = server.next_request() # Request shall be a POST assert rq.method == "POST", "Expecting POST, got %s" % rq # Request shall be for base + 'api/[email protected]' assert rq.uri == BASE_URI + "api/read" # Accept header shall be given assert rq.headers[b"Accept"] == "text/zinc" assert rq.headers[b"Content-Type"] == "text/zinc" # Body shall be a single grid: rq_grid = hszinc.parse(rq.body.decode("utf-8"), mode=hszinc.MODE_ZINC, single=True) # It shall have one column; id assert set(rq_grid.column.keys()) == set(["id"]) # It shall have 2 rows assert len(rq_grid) == 2 # Each row should only have 'id' values assert all([(set(r.keys()) == set(["id"])) for r in rq_grid]) # The rows' 'id' column should *only* contain Refs. assert all([isinstance(r["id"], hszinc.Ref) for r in rq_grid]) # Both IDs shall be listed, we don't about order assert set([r["id"].name for r in rq_grid ]) == set(["my.entity.id1", "my.entity.id2"]) # Make a grid to respond with response = hszinc.Grid() response.column["id"] = {} response.column["dis"] = {} response.column["randomTag"] = {} response.extend([ { "id": hszinc.Ref("my.entity.id1", value="id1"), "dis": "A test entity #1", "randomTag": hszinc.MARKER, }, { "id": hszinc.Ref("my.entity.id2", value="id2"), "dis": "A test entity #2", "randomTag": hszinc.MARKER, }, ]) rq.respond( status=200, headers={b"Content-Type": "text/zinc"}, content=hszinc.dump(response, mode=hszinc.MODE_ZINC), ) # State machine should now be done assert op.is_done entities = op.result # Response should be a dict assert isinstance(entities, dict), "%r not a dict" % entities # Response should have these keys assert set(entities.keys()) == set(["my.entity.id1", "my.entity.id2"]) entity = entities.pop("my.entity.id1") assert isinstance(entity, Entity), "%r not an entity" % entity # The tags should be passed through from the response assert entity.id.name == "my.entity.id1" assert entity.tags["dis"] == response[0]["dis"] assert entity.tags["randomTag"] == response[0]["randomTag"] entity = entities.pop("my.entity.id2") assert isinstance(entity, Entity), "%r not an entity" % entity # The tags should be passed through from the response assert entity.id.name == "my.entity.id2" assert entity.tags["dis"] == response[1]["dis"] assert entity.tags["randomTag"] == response[1]["randomTag"]
def test_data_types_json_v3(): grid = hszinc.Grid(version=hszinc.VER_3_0) grid.column['comment'] = {} grid.column['value'] = {} grid.extend([ { 'comment': 'An empty list', 'value': [], }, { 'comment': 'A null value in a list', 'value': [None], }, { 'comment': 'A marker in a list', 'value': [hszinc.MARKER], }, { 'comment': 'Booleans', 'value': [True, False], }, { 'comment': 'References', 'value': [hszinc.Ref('a-ref'), hszinc.Ref('a-ref', 'a value')], }, { 'comment': 'A quantity', 'value': [hszinc.Quantity(500,'miles')], }, ]) grid_json = json.loads(hszinc.dump(grid, mode=hszinc.MODE_JSON)) assert grid_json == { 'meta': { 'ver': '3.0' }, 'cols': [ {'name': 'comment'}, {'name': 'value'}, ], 'rows': [ { 'comment':"s:An empty list", 'value':[] }, { 'comment':"s:A null value in a list", 'value': [None] }, { 'comment':"s:A marker in a list", 'value': ['m:'] }, { 'comment':"s:Booleans", 'value': [True, False] }, { 'comment':"s:References", 'value': ['r:a-ref' , 'r:a-ref a value'] }, { 'comment':"s:A quantity", 'value': ['n:500.000000 miles'] # Python is more precise # than The Proclaimers } ] }
def test_ref_withdis_neq_dis(): r1 = hszinc.Ref(name='a.ref', value='display text') r2 = hszinc.Ref(name='a.ref', value='different display text') assert r1 is not r2 assert r1 != r2
def json_decode(raw_json): ''' Recusively scan the de-serialised JSON tree for objects that correspond to standard Haystack types and return the Python equivalents. ''' # Simple cases if raw_json is None: return None elif raw_json == MARKER: return hszinc.MARKER elif isinstance(raw_json, dict): result = {} for key, value in raw_json.items(): result[json_decode(key)] = json_decode(value) return result elif isinstance(raw_json, list): return list(map(json_decode, raw_json)) elif isinstance(raw_json, tuple): return tuple(map(json_decode, raw_json)) # Is it a number? match = NUMBER_RE.match(raw_json) if match: # We'll get a value and a unit, amongst other tokens. matched = match.groups() value = float(matched[0]) if matched[-1] is not None: # It's a quantity return hszinc.Quantity(value, matched[-1]) else: # It's a raw value return value # Is it a string? match = STR_RE.match(raw_json) if match: return match.group(1) # Is it a reference? match = REF_RE.match(raw_json) if match: matched = match.groups() if matched[-1] is not None: return hszinc.Ref(matched[0], matched[-1], has_value=True) else: return hszinc.Ref(matched[0]) # Is it a date? match = DATE_RE.match(raw_json) if match: (year, month, day) = match.groups() return datetime.date(year=int(year), month=int(month), day=int(day)) # Is it a time? match = TIME_RE.match(raw_json) if match: (hour, minute, second) = match.groups() # Convert second to seconds and microseconds second = float(second) int_second = int(second) second -= int_second microsecond = second * 1e6 return datetime.time(hour=int(hour), minute=int(minute), second=int_second, microseconds=microsecond) # Is it a date/time? match = DATETIME_RE.match(raw_json) if match: matches = match.groups() # Parse ISO8601 component isodate = iso8601.parse_date(matches[0]) # Parse timezone tzname = matches[-1] if tzname is None: return isodate # No timezone given else: try: tz = zoneinfo.timezone(tzname) return isodate.astimezone(tz) except: return isodate # Is it a URI? match = URI_RE.match(raw_json) if match: return hszinc.Uri(match.group(1)) # Is it a Bin? match = BIN_RE.match(raw_json) if match: return hszinc.Bin(match.group(1)) # Is it a co-ordinate? match = COORD_RE.match(raw_json) if match: (lat, lng) = match.groups() return hszinc.Coord(lat, lng) # Maybe it's a bare string? return raw_json
def test_ref_withdis_eq(): r1 = hszinc.Ref(name='a.ref', value='display text') r2 = hszinc.Ref(name='a.ref', value='display text') assert r1 is not r2 assert r1 == r2
def test_ref_withdis_neq_id(): r1 = hszinc.Ref(name='a.ref', value='display text') r2 = hszinc.Ref(name='another.ref', value='display text') assert r1 is not r2 assert r1 != r2
def test_ref_simple_neq_id(): r1 = hszinc.Ref(name='a.ref') r2 = hszinc.Ref(name='another.ref') assert r1 is not r2 assert r1 != r2
def test_ref_mixed_neq(): r1 = hszinc.Ref(name='a.ref') r2 = hszinc.Ref(name='a.ref', value='display text') assert r1 is not r2 assert r1 != r2
def test_ref_simple_eq(): r1 = hszinc.Ref(name='a.ref') r2 = hszinc.Ref(name='a.ref') assert r1 is not r2 assert r1 == r2
def test_ref_notref_ne(): r1 = hszinc.Ref(name='a.ref') r2 = 'not.a.ref' assert r1 is not r2 assert r1 != r2
def test_ref_notref_eq(): r1 = hszinc.Ref(name='a.ref') r2 = 'not.a.ref' assert r1 is not r2 assert not (r1 == r2)
def test_scalar_json(): # No need to be exhaustive, the underlying function is tested heavily by # the grid dump tests. assert hszinc.dump_scalar(hszinc.Ref('areference', 'a display name'), mode=hszinc.MODE_JSON) == 'r:areference a display name'
def test_ref_hash(): assert hash(hszinc.Ref(name='a.ref', value='display text')) == \ hash('a.ref') ^ hash('display text') ^ hash(True)