def dehydrate_datetime(value): """ Dehydrator for `datetime` values. :param value: :type value: datetime :return: """ def seconds_and_nanoseconds(dt): if isinstance(dt, datetime): dt = DateTime.from_native(dt) zone_epoch = DateTime(1970, 1, 1, tzinfo=dt.tzinfo) t = dt.to_clock_time() - zone_epoch.to_clock_time() return t.seconds, t.nanoseconds tz = value.tzinfo if tz is None: # without time zone value = utc.localize(value) seconds, nanoseconds = seconds_and_nanoseconds(value) return Structure(b"d", seconds, nanoseconds) elif hasattr(tz, "zone") and tz.zone: # with named time zone seconds, nanoseconds = seconds_and_nanoseconds(value) return Structure(b"f", seconds, nanoseconds, tz.zone) else: # with time offset seconds, nanoseconds = seconds_and_nanoseconds(value) return Structure(b"F", seconds, nanoseconds, tz.utcoffset(value).seconds)
def json_to_packstream(cls, data): """ This converts from JSON format into PackStream prior to proper hydration. This code needs to die horribly in a freak yachting accident. """ # TODO: other partial hydration if "self" in data: if "type" in data: return Structure(b"R", uri_to_id(data["self"]), uri_to_id(data["start"]), uri_to_id(data["end"]), data["type"], data["data"]) else: return Structure(b"N", uri_to_id(data["self"]), data["metadata"]["labels"], data["data"]) elif "nodes" in data and "relationships" in data: nodes = [Structure(b"N", i, None, None) for i in map(uri_to_id, data["nodes"])] relps = [Structure(b"r", i, None, None) for i in map(uri_to_id, data["relationships"])] seq = [i // 2 + 1 for i in range(2 * len(data["relationships"]))] for i, direction in enumerate(data["directions"]): if direction == "<-": seq[2 * i] *= -1 return Structure(b"P", nodes, relps, seq) else: # from warnings import warn # warn("Map literals returned over the Neo4j HTTP interface are ambiguous " # "and may be unintentionally hydrated as graph objects") return data
def dehydrate_point(value): """ Dehydrator for Point data. :param value: :type value: Point :return: """ dim = len(value) if dim == 2: return Structure(b"X", value.srid, *value) elif dim == 3: return Structure(b"Y", value.srid, *value) else: raise ValueError("Cannot dehydrate Point with %d dimensions" % dim)
def test_can_hydrate_node_structure(self): struct = Structure(b'N', 123, ["Person"], {"name": "Alice"}) alice, = self.hydrant.hydrate([struct]) self.assertEqual(alice.id, 123) self.assertEqual(alice.labels, {"Person"}) self.assertEqual(set(alice.keys()), {"name"}) self.assertEqual(alice.get("name"), "Alice")
def dehydrate_date(value): """ Dehydrator for `date` values. :param value: :type value: Date :return: """ return Structure(b"D", value.toordinal() - UNIX_EPOCH_DATE.toordinal())
def dehydrate_duration(value): """ Dehydrator for `duration` values. :param value: :type value: Duration :return: """ return Structure(b"E", value.months, value.days, value.seconds, int(1000000000 * value.subseconds))
def test_can_hydrate_date_time_structure(self): struct = Structure(b'd', 1539344261, 474716862) dt, = self.hydrant.hydrate([struct]) self.assertEqual(dt.year, 2018) self.assertEqual(dt.month, 10) self.assertEqual(dt.day, 12) self.assertEqual(dt.hour, 11) self.assertEqual(dt.minute, 37) self.assertEqual(dt.second, 41.474716862)
def test_can_hydrate_in_dict(self): struct = Structure(b'N', 123, ["Person"], {"name": "Alice"}) alice_in_dict, = self.hydrant.hydrate([{"foo": struct}]) self.assertIsInstance(alice_in_dict, dict) alice = alice_in_dict["foo"] self.assertEqual(alice.id, 123) self.assertEqual(alice.labels, {"Person"}) self.assertEqual(set(alice.keys()), {"name"}) self.assertEqual(alice.get("name"), "Alice")
def test_can_hydrate_in_list(self): struct = Structure(b'N', 123, ["Person"], {"name": "Alice"}) alice_in_list, = self.hydrant.hydrate([[struct]]) self.assertIsInstance(alice_in_list, list) alice, = alice_in_list self.assertEqual(alice.id, 123) self.assertEqual(alice.labels, {"Person"}) self.assertEqual(set(alice.keys()), {"name"}) self.assertEqual(alice.get("name"), "Alice")
def dehydrate_time(value): """ Dehydrator for `time` values. :param value: :type value: Time :return: """ if isinstance(value, Time): nanoseconds = int(value.ticks * 1000000000) elif isinstance(value, time): nanoseconds = (3600000000000 * value.hour + 60000000000 * value.minute + 1000000000 * value.second + 1000 * value.microsecond) else: raise TypeError("Value must be a neotime.Time or a datetime.time") if value.tzinfo: return Structure(b"T", nanoseconds, value.tzinfo.utcoffset(value).seconds) else: return Structure(b"t", nanoseconds)
def dehydrate_timedelta(value): """ Dehydrator for `timedelta` values. :param value: :type value: timedelta :return: """ months = 0 days = value.days seconds = value.seconds nanoseconds = 1000 * value.microseconds return Structure(b"E", months, days, seconds, nanoseconds)
def test_tiny_struct(self): self.assert_packable(Structure(b"Z", u"A", 1), b"\xB2Z\x81A\x01")
def test_empty_struct(self): self.assert_packable(Structure(b"X"), b"\xB0X")
def test_illegal_signature(self): with self.assertRaises(ValueError): self.assert_packable(Structure(b"XXX"), b"\xB0XXX")
def test_hydrating_unknown_structure_returns_same(self): struct = Structure(b'?', "foo") mystery, = self.hydrant.hydrate([struct]) self.assertEqual(mystery, struct)
def _unpack(self): marker = self.read_int() if marker == -1: raise RuntimeError("Nothing to unpack") # Tiny Integer if 0x00 <= marker <= 0x7F: return marker elif 0xF0 <= marker <= 0xFF: return marker - 0x100 # Null elif marker == 0xC0: return None # Float elif marker == 0xC1: value, = struct_unpack(">d", self.read(8)) return value # Boolean elif marker == 0xC2: return False elif marker == 0xC3: return True # Integer elif marker == 0xC8: return struct_unpack(">b", self.read(1))[0] elif marker == 0xC9: return struct_unpack(">h", self.read(2))[0] elif marker == 0xCA: return struct_unpack(">i", self.read(4))[0] elif marker == 0xCB: return struct_unpack(">q", self.read(8))[0] # Bytes elif marker == 0xCC: size, = struct_unpack(">B", self.read(1)) return self.read(size).tobytes() elif marker == 0xCD: size, = struct_unpack(">H", self.read(2)) return self.read(size).tobytes() elif marker == 0xCE: size, = struct_unpack(">I", self.read(4)) return self.read(size).tobytes() else: marker_high = marker & 0xF0 # String if marker_high == 0x80: # TINY_STRING return decode(self.read(marker & 0x0F), "utf-8") elif marker == 0xD0: # STRING_8: size, = struct_unpack(">B", self.read(1)) return decode(self.read(size), "utf-8") elif marker == 0xD1: # STRING_16: size, = struct_unpack(">H", self.read(2)) return decode(self.read(size), "utf-8") elif marker == 0xD2: # STRING_32: size, = struct_unpack(">I", self.read(4)) return decode(self.read(size), "utf-8") # List elif 0x90 <= marker <= 0x9F or 0xD4 <= marker <= 0xD7: return self._unpack_list(marker) # Map elif 0xA0 <= marker <= 0xAF or 0xD8 <= marker <= 0xDB: return self._unpack_map(marker) # Structure elif 0xB0 <= marker <= 0xBF or 0xDC <= marker <= 0xDD: size, tag = self._unpack_structure_header(marker) value = Structure(tag, *([None] * size)) for i in range(len(value)): value[i] = self._unpack() return value elif marker == 0xDF: # END_OF_STREAM: return EndOfStream else: raise RuntimeError("Unknown PackStream marker %02X" % marker)