Skip to content

Commit e19c686

Browse files
committed
Merge branch '4-vector-types' into 'main'
Support vector types #4 See merge request objectbox/objectbox-python!5
2 parents c94ac96 + 06dfb8d commit e19c686

11 files changed

+100
-20
lines changed

.gitlab-ci.yml

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ build:
3030
stage: test
3131
script:
3232
- pip3 install --user pytest
33+
- pip3 install --user numpy
3334
- rm -r objectbox
3435
- pip3 install --user --force-reinstall dist/*.whl
3536
- ${PYTHON} -m pytest
@@ -59,9 +60,9 @@ test:linux:x64:3.10:
5960
extends: .test:linux:x64
6061
image: python:3.10
6162

62-
test:linux:armv7hf:
63-
extends: .test-python3
64-
tags: [armv7hf, shell, linux, python3]
63+
# test:linux:armv7hf:
64+
# extends: .test-python3
65+
# tags: [armv7hf, shell, linux, python3]
6566

6667
# aarch64 version not published
6768
#test:linux:aarch64:
@@ -71,6 +72,9 @@ test:linux:armv7hf:
7172
test:mac:x64:
7273
extends: .test-python3
7374
tags: [mac, x64, shell, python3]
75+
script:
76+
- make depend
77+
- make test
7478

7579
test:windows:x64:
7680
extends: .test

download-c-lib.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
# Script used to download objectbox-c shared libraries for all supported platforms. Execute by running `make get-lib`
77
# on first checkout of this repo and any time after changing the objectbox-c lib version.
88

9-
version = "v0.14.0" # see objectbox/c.py required_version
9+
version = "v0.18.1" # see objectbox/c.py required_version
1010
variant = 'objectbox' # or 'objectbox-sync'
1111

1212
base_url = "https://github.com/objectbox/objectbox-c/releases/download/"

objectbox/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright 2019-2021 ObjectBox Ltd. All rights reserved.
1+
# Copyright 2019-2023 ObjectBox Ltd. All rights reserved.
22
#
33
# Licensed under the Apache License, Version 2.0 (the "License");
44
# you may not use this file except in compliance with the License.
@@ -31,7 +31,7 @@
3131
]
3232

3333
# Python binding version
34-
version = Version(0, 4, 0)
34+
version = Version(0, 5, 0)
3535

3636

3737
def version_info():

objectbox/c.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424

2525
# Version of the library used by the binding. This version is checked at runtime to ensure binary compatibility.
2626
# Don't forget to update download-c-lib.py when upgrading to a newer version.
27-
required_version = "0.14.0"
27+
required_version = "0.18.1"
2828

2929

3030
def shlib_name(library: str) -> str:
@@ -395,6 +395,10 @@ def c_voidp_as_bytes(voidp, size):
395395
OBXPropertyType_Date = 10
396396
OBXPropertyType_Relation = 11
397397
OBXPropertyType_ByteVector = 23
398+
OBXPropertyType_IntVector = 26
399+
OBXPropertyType_LongVector = 27
400+
OBXPropertyType_FloatVector = 28
401+
OBXPropertyType_DoubleVector = 29
398402
OBXPropertyType_StringVector = 30
399403

400404
OBXPropertyFlags_ID = 1

objectbox/model/entity.py

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright 2019-2021 ObjectBox Ltd. All rights reserved.
1+
# Copyright 2019-2023 ObjectBox Ltd. All rights reserved.
22
#
33
# Licensed under the Apache License, Version 2.0 (the "License");
44
# you may not use this file except in compliance with the License.
@@ -14,6 +14,7 @@
1414

1515

1616
import flatbuffers
17+
import numpy as np
1718
from objectbox.c import *
1819
from objectbox.model.properties import Property
1920

@@ -64,8 +65,14 @@ def fill_properties(self):
6465
self.id_property = prop
6566

6667
if prop._fb_type == flatbuffers.number_types.UOffsetTFlags:
67-
assert prop._ob_type in [OBXPropertyType_String, OBXPropertyType_ByteVector], \
68-
"programming error - invalid type OB & FB type combination"
68+
assert prop._ob_type in [
69+
OBXPropertyType_String,
70+
OBXPropertyType_ByteVector,
71+
OBXPropertyType_IntVector,
72+
OBXPropertyType_LongVector,
73+
OBXPropertyType_FloatVector,
74+
OBXPropertyType_DoubleVector,
75+
], "programming error - invalid type OB & FB type combination"
6976
self.offset_properties.append(prop)
7077

7178
# print('Property {}.{}: {} (ob:{} fb:{})'.format(self.name, prop._name, prop._py_type, prop._ob_type, prop._fb_type))
@@ -78,8 +85,11 @@ def fill_properties(self):
7885
def get_value(self, object, prop: Property):
7986
# in case value is not overwritten on the object, it's the Property object itself (= as defined in the Class)
8087
val = getattr(object, prop._name)
81-
if val == prop:
82-
return prop._py_type() # default (empty) value for the given type
88+
if prop._py_type == np.ndarray:
89+
if (val == np.array(prop)).all():
90+
return np.array([])
91+
elif val == prop:
92+
return prop._py_type() # default (empty) value for the given type
8393
return val
8494

8595
def get_object_id(self, object) -> int:
@@ -99,6 +109,14 @@ def marshal(self, object, id: int) -> bytearray:
99109
offsets[prop._id] = builder.CreateString(val.encode('utf-8'))
100110
elif prop._ob_type == OBXPropertyType_ByteVector:
101111
offsets[prop._id] = builder.CreateByteVector(val)
112+
elif prop._ob_type == OBXPropertyType_IntVector:
113+
offsets[prop._id] = builder.CreateNumpyVector(np.array(val, dtype=np.int32))
114+
elif prop._ob_type == OBXPropertyType_LongVector:
115+
offsets[prop._id] = builder.CreateNumpyVector(np.array(val, dtype=np.int64))
116+
elif prop._ob_type == OBXPropertyType_FloatVector:
117+
offsets[prop._id] = builder.CreateNumpyVector(np.array(val, dtype=np.float32))
118+
elif prop._ob_type == OBXPropertyType_DoubleVector:
119+
offsets[prop._id] = builder.CreateNumpyVector(np.array(val, dtype=np.float64))
102120
else:
103121
assert False, "programming error - invalid type OB & FB type combination"
104122

@@ -140,12 +158,20 @@ def unmarshal(self, data: bytes):
140158
# access the FB byte vector information
141159
start = table.Vector(o)
142160
size = table.VectorLen(o)
143-
144161
# slice the vector as a requested type
145162
val = prop._py_type(table.Bytes[start:start+size])
163+
elif prop._ob_type == OBXPropertyType_IntVector:
164+
val = table.GetVectorAsNumpy(flatbuffers.number_types.Int32Flags, o)
165+
elif prop._ob_type == OBXPropertyType_LongVector:
166+
val = table.GetVectorAsNumpy(flatbuffers.number_types.Int64Flags, o)
167+
elif prop._ob_type == OBXPropertyType_FloatVector:
168+
val = table.GetVectorAsNumpy(flatbuffers.number_types.Float32Flags, o)
169+
elif prop._ob_type == OBXPropertyType_DoubleVector:
170+
val = table.GetVectorAsNumpy(flatbuffers.number_types.Float64Flags, o)
146171
else:
147172
val = table.Get(prop._fb_type, o + table.Pos)
148-
173+
if prop._py_type == list:
174+
val = val.tolist()
149175
setattr(obj, prop._name, val)
150176
return obj
151177

objectbox/model/properties.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright 2019-2021 ObjectBox Ltd. All rights reserved.
1+
# Copyright 2019-2023 ObjectBox Ltd. All rights reserved.
22
#
33
# Licensed under the Apache License, Version 2.0 (the "License");
44
# you may not use this file except in compliance with the License.
@@ -16,6 +16,7 @@
1616

1717
from objectbox.c import *
1818
import flatbuffers.number_types
19+
import numpy as np
1920

2021

2122
class PropertyType(IntEnum):
@@ -31,6 +32,10 @@ class PropertyType(IntEnum):
3132
# date = OBXPropertyType_Date
3233
# relation = OBXPropertyType_Relation
3334
byteVector = OBXPropertyType_ByteVector
35+
intVector = OBXPropertyType_IntVector
36+
longVector = OBXPropertyType_LongVector
37+
floatVector = OBXPropertyType_FloatVector
38+
doubleVector = OBXPropertyType_DoubleVector
3439
# stringVector = OBXPropertyType_StringVector
3540

3641

@@ -47,6 +52,10 @@ class PropertyType(IntEnum):
4752
# PropertyType.date: flatbuffers.number_types.Int64Flags,
4853
# PropertyType.relation: flatbuffers.number_types.Int64Flags,
4954
PropertyType.byteVector: flatbuffers.number_types.UOffsetTFlags,
55+
PropertyType.intVector: flatbuffers.number_types.UOffsetTFlags,
56+
PropertyType.longVector: flatbuffers.number_types.UOffsetTFlags,
57+
PropertyType.floatVector: flatbuffers.number_types.UOffsetTFlags,
58+
PropertyType.doubleVector: flatbuffers.number_types.UOffsetTFlags,
5059
# PropertyType.stringVector: flatbuffers.number_types.UOffsetTFlags,
5160
}
5261

@@ -77,6 +86,8 @@ def __determine_ob_type(self) -> OBXPropertyType:
7786
return OBXPropertyType_Long
7887
elif ts == bytes: # or ts == bytearray: might require further tests on read objects due to mutability
7988
return OBXPropertyType_ByteVector
89+
elif ts == list or ts == np.ndarray:
90+
return OBXPropertyType_DoubleVector
8091
elif ts == float:
8192
return OBXPropertyType_Double
8293
elif ts == bool:

requirements.txt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
pip
22
wheel
3-
flatbuffers==2.0
3+
flatbuffers==23.5.26
44
pytest>=4.4.1
5-
twine
5+
twine
6+
numpy

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
],
4747

4848
install_requires=[
49-
'flatbuffers==2.0',
49+
'flatbuffers==23.5.26',
5050
],
5151

5252
packages=setuptools.find_packages(),

tests/common.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import shutil
44
import pytest
55
from tests.model import TestEntity
6+
import numpy as np
67

78
test_dir = 'testdata'
89

@@ -23,7 +24,7 @@ def autocleanup():
2324
def load_empty_test_objectbox(name: str = "") -> objectbox.ObjectBox:
2425
model = objectbox.Model()
2526
from objectbox.model import IdUid
26-
model.entity(TestEntity, last_property_id=IdUid(10, 1010))
27+
model.entity(TestEntity, last_property_id=IdUid(18, 1018))
2728
model.last_entity_id = IdUid(1, 1)
2829

2930
db_name = test_dir if len(name) == 0 else test_dir + "/" + name
@@ -35,6 +36,14 @@ def assert_equal_prop(actual, expected, default):
3536
assert actual == expected or (isinstance(
3637
expected, objectbox.model.Property) and actual == default)
3738

39+
def assert_equal_prop_vector(actual, expected, default):
40+
assert (actual == np.array(expected)).all() or (isinstance(
41+
expected, objectbox.model.Property) and actual == default)
42+
43+
# compare approx values for list
44+
def assert_equal_prop_list(actual, expected, default):
45+
assert pytest.approx(actual) == expected or (isinstance(
46+
expected, objectbox.model.Property) and actual == default)
3847

3948
def assert_equal(actual: TestEntity, expected: TestEntity):
4049
"""Check that two TestEntity objects have the same property data"""
@@ -46,3 +55,11 @@ def assert_equal(actual: TestEntity, expected: TestEntity):
4655
assert_equal_prop(actual.float64, expected.float64, 0)
4756
assert_equal_prop(actual.float32, expected.float32, 0)
4857
assert_equal_prop(actual.bytes, expected.bytes, b'')
58+
assert_equal_prop_vector(actual.ints, expected.ints, np.array([]))
59+
assert_equal_prop_vector(actual.longs, expected.longs, np.array([]))
60+
assert_equal_prop_vector(actual.floats, expected.floats, np.array([]))
61+
assert_equal_prop_vector(actual.doubles, expected.doubles, np.array([]))
62+
assert_equal_prop_list(actual.ints_list, expected.ints_list, [])
63+
assert_equal_prop_list(actual.longs_list, expected.longs_list, [])
64+
assert_equal_prop_list(actual.floats_list, expected.floats_list, [])
65+
assert_equal_prop_list(actual.doubles_list, expected.doubles_list, [])

tests/model.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from objectbox.model import *
2+
import numpy as np
23

34

45
@Entity(id=1, uid=1)
@@ -13,6 +14,14 @@ class TestEntity:
1314
float64 = Property(float, type=PropertyType.double, id=8, uid=1008)
1415
float32 = Property(float, type=PropertyType.float, id=9, uid=1009)
1516
bytes = Property(bytes, id=10, uid=1010)
17+
ints = Property(np.ndarray, type=PropertyType.intVector, id=11, uid=1011)
18+
longs = Property(np.ndarray, type=PropertyType.longVector, id=12, uid=1012)
19+
floats = Property(np.ndarray, type=PropertyType.floatVector, id=13, uid=1013)
20+
doubles = Property(np.ndarray, type=PropertyType.doubleVector, id=14, uid=1014)
21+
ints_list = Property(list, type=PropertyType.intVector, id=15, uid=1015)
22+
longs_list = Property(list, type=PropertyType.longVector, id=16, uid=1016)
23+
floats_list = Property(list, type=PropertyType.floatVector, id=17, uid=1017)
24+
doubles_list = Property(list, type=PropertyType.doubleVector, id=18, uid=1018)
1625
transient = "" # not "Property" so it's not stored
1726

1827
def __init__(self, string: str = ""):

tests/test_box.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import pytest
22
import objectbox
3+
import numpy as np
34
from tests.model import TestEntity
45
from tests.common import autocleanup, load_empty_test_objectbox, assert_equal
56

@@ -29,12 +30,19 @@ def test_box_basics():
2930
object.float64 = 4.2
3031
object.float32 = 1.5
3132
object.bytes = bytes([1, 1, 2, 3, 5])
33+
object.ints = np.array([1, 2, 3, 555, 120, 222], dtype=np.int32)
34+
object.longs = np.array([9182, 8273, 7364, 6455, 55462547], dtype=np.int64)
35+
object.floats = np.array([0.1, 1.2, 2.3, 3.4, 4.5], dtype=np.float32)
36+
object.doubles = np.array([99.99, 88.88, 77.77, 66.66, 55.595425], dtype=np.float64)
37+
object.ints_list = [91, 82, 73, 64, 55]
38+
object.longs_list = [4568, 8714, 1234, 5678, 9012240941]
39+
object.floats_list = [0.11, 1.22, 2.33, 3.44, 4.5595]
40+
object.doubles_list = [99.1999, 88.2888, 77.3777, 66.4666, 55.6597555]
3241
object.transient = "abcd"
3342

3443
id = box.put(object)
3544
assert id == 5
3645
assert id == object.id
37-
3846
# check the count
3947
assert not box.is_empty()
4048
assert box.count() == 2

0 commit comments

Comments
 (0)