Exemple #1
0
    def recompose_base(self, obj: dict) -> Base:
        """Steps through a base object dictionary and recomposes the base object

        Arguments:
            obj {dict} -- the dictionary representation of the object

        Returns:
            Base -- the base object with all its children attached
        """
        # make sure an obj was passed and create dict if string was somehow passed
        if not obj:
            return
        if isinstance(obj, str):
            obj = safe_json_loads(obj)

        if "speckle_type" in obj and obj["speckle_type"] == "reference":
            obj = self.get_child(obj=obj)

        speckle_type = obj.get("speckle_type")
        # if speckle type is not in the object definition, it is treated as a dict
        if not speckle_type:
            return obj

        # get the registered type from base register.
        object_type = Base.get_registered_type(speckle_type)

        # initialise the base object using `speckle_type` fall back to base if needed
        base = object_type() if object_type else Base.of_type(
            speckle_type=speckle_type)
        # get total children count
        if "__closure" in obj:
            if not self.read_transport:
                raise SpeckleException(
                    message=
                    "Cannot resolve reference - no read transport is defined")
            closure = obj.pop("__closure")
            base.totalChildrenCount = len(closure)

        for prop, value in obj.items():
            # 1. handle primitives (ints, floats, strings, and bools) or None
            if isinstance(value, PRIMITIVES) or value is None:
                base.__setattr__(prop, value)
                continue

            # 2. handle referenced child objects
            elif "referencedId" in value:
                ref_hash = value["referencedId"]
                ref_obj_str = self.read_transport.get_object(id=ref_hash)
                if not ref_obj_str:
                    raise SpeckleException(
                        f"Could not find the referenced child object of id `{ref_hash}` in the given read transport: {self.read_transport.name}"
                    )
                ref_obj = safe_json_loads(ref_obj_str, ref_hash)
                base.__setattr__(prop, self.recompose_base(obj=ref_obj))

            # 3. handle all other cases (base objects, lists, and dicts)
            else:
                base.__setattr__(prop, self.handle_value(value))

        return base
Exemple #2
0
def test_setting_units():
    b = Base(units="foot")
    assert b.units == "ft"

    with pytest.raises(SpeckleException):
        b.units = "big"

    b.units = None  # invalid args are skipped
    b.units = 7
    assert b.units == "ft"
Exemple #3
0
def base():
    base = Base()
    base.name = "my_base"
    base.units = "millimetres"
    base.vertices = [random.uniform(0, 10) for _ in range(1, 120)]
    base.test_bases = [Base(name=i) for i in range(1, 22)]
    base["@detach"] = Base(name="detached base")
    base["@revit_thing"] = Base.of_type("SpecialRevitFamily",
                                        name="secret tho")
    return base
Exemple #4
0
def base():
    base = Base()
    base.name = "my_base"
    base.units = "millimetres"
    base.vertices = [random.uniform(0, 10) for _ in range(1, 120)]
    base.test_bases = [Base(name=i) for i in range(1, 22)]
    base["@detach"] = Base(name="detached base")
    return base
Exemple #5
0
def test_invalid_send():
    client = SpeckleClient()
    client.account = Account(token="fake_token")
    transport = ServerTransport("3073b96e86", client)

    with pytest.raises(SpeckleException):
        operations.send(Base(), [transport])
Exemple #6
0
def mesh():
    mesh = FakeMesh()
    mesh.name = "my_mesh"
    mesh.vertices = [random.uniform(0, 10) for _ in range(1, 210)]
    mesh.faces = [i for i in range(1, 210)]
    mesh["@(100)colours"] = [random.uniform(0, 10) for _ in range(1, 210)]
    mesh["@()default_chunk"] = [random.uniform(0, 10) for _ in range(1, 210)]
    mesh.test_bases = [Base(name=f"test {i}") for i in range(1, 22)]
    mesh.detach_this = Base(name="predefined detached base")
    mesh["@detach"] = Base(name="detached base")
    mesh["@detached_list"] = [
        42,
        "some text",
        [1, 2, 3],
        Base(name="detached within a list"),
    ]
    mesh.origin = Point(x=4, y=2)
    return mesh
Exemple #7
0
 def from_curve(cls, curve: Base) -> "CurveArray":
     crv_array = cls()
     crv_array.data = curve.to_list()
     return crv_array
Exemple #8
0
 def encode_object(self, object: Base):
     encoded = object.to_list()
     encoded.insert(0, len(encoded))
     self.data.extend(encoded)
Exemple #9
0
def test_base_of_custom_speckle_type() -> None:
    b1 = Base.of_type("BirdHouse", name="Tweety's Crib")
    assert b1.speckle_type == "BirdHouse"
    assert b1.name == "Tweety's Crib"
Exemple #10
0
def test_speckle_type_cannot_be_set(base: Base) -> None:
    assert base.speckle_type == "Base"
    base.speckle_type = "unset"
    assert base.speckle_type == "Base"
Exemple #11
0
def test_new_type_registration() -> None:
    """Test if a new subclass is registered into the type register."""
    assert Base.get_registered_type("FakeModel") == FakeModel
    assert Base.get_registered_type("🐺️") is None
Exemple #12
0
def test_empty_prop_names(invalid_prop_name: str) -> None:
    base = Base()
    with pytest.raises(ValueError):
        base[invalid_prop_name] = "🐛️"
Exemple #13
0
    def traverse_base(self, base: Base) -> Tuple[str, Dict]:
        """Decomposes the given base object and builds a serializable dictionary

        Arguments:
            base {Base} -- the base object to be decomposed and serialized

        Returns:
            (str, dict) -- a tuple containing the hash (id) of the base object and the constructed serializable dictionary
        """
        if not self.detach_lineage:
            self.detach_lineage = [True]

        self.lineage.append(uuid4().hex)
        object_builder = {"id": "", "speckle_type": "Base", "totalChildrenCount": 0}
        object_builder.update(speckle_type=base.speckle_type)
        obj, props = base, base.get_member_names()

        while props:
            prop = props.pop(0)
            value = getattr(obj, prop, None)
            chunkable = False
            detach = False

            # skip nulls or props marked to be ignored with "__" or "_"
            if value is None or prop.startswith(("__", "_")):
                continue

            # don't prepopulate id as this will mess up hashing
            if prop == "id":
                continue

            # only bother with chunking and detaching if there is a write transport
            if self.write_transports:
                dynamic_chunk_match = re.match(r"^@\((\d*)\)", prop)
                if dynamic_chunk_match:
                    chunk_size = dynamic_chunk_match.groups()[0]
                    base._chunkable[prop] = (
                        int(chunk_size) if chunk_size else base._chunk_size_default
                    )

                chunkable = prop in base._chunkable
                detach = bool(
                    prop.startswith("@") or prop in base._detachable or chunkable
                )

            # 1. handle primitives (ints, floats, strings, and bools)
            if isinstance(value, PRIMITIVES):
                object_builder[prop] = value
                continue

            # 2. handle Base objects
            elif isinstance(value, Base):
                child_obj = self.traverse_value(value, detach=detach)
                if detach and self.write_transports:
                    ref_hash = child_obj["id"]
                    object_builder[prop] = self.detach_helper(ref_hash=ref_hash)
                else:
                    object_builder[prop] = child_obj

            # 3. handle chunkable props
            elif chunkable and self.write_transports:
                chunks = []
                max_size = base._chunkable[prop]
                chunk = DataChunk()
                for count, item in enumerate(value):
                    if count and count % max_size == 0:
                        chunks.append(chunk)
                        chunk = DataChunk()
                    chunk.data.append(item)
                chunks.append(chunk)

                chunk_refs = []
                for c in chunks:
                    self.detach_lineage.append(detach)
                    ref_hash, _ = self.traverse_base(c)
                    ref_obj = self.detach_helper(ref_hash=ref_hash)
                    chunk_refs.append(ref_obj)
                object_builder[prop] = chunk_refs

            # 4. handle all other cases
            else:
                child_obj = self.traverse_value(value, detach)
                object_builder[prop] = child_obj

        closure = {}
        # add closures & children count to the object
        detached = self.detach_lineage.pop()
        if self.lineage[-1] in self.family_tree:
            closure = {
                ref: depth - len(self.detach_lineage)
                for ref, depth in self.family_tree[self.lineage[-1]].items()
            }
        object_builder["totalChildrenCount"] = len(closure)

        hash = hash_obj(object_builder)

        object_builder["id"] = hash
        if closure:
            object_builder["__closure"] = self.closure_table[hash] = closure

        # write detached or root objects to transports
        if detached and self.write_transports:
            for t in self.write_transports:
                t.save_object(id=hash, serialized_object=json.dumps(object_builder))

        del self.lineage[-1]

        return hash, object_builder