diff --git a/src/blenderbim/blenderbim/bim/import_ifc.py b/src/blenderbim/blenderbim/bim/import_ifc.py
index 64846215e4..fb7c07d7cd 100644
--- a/src/blenderbim/blenderbim/bim/import_ifc.py
+++ b/src/blenderbim/blenderbim/bim/import_ifc.py
@@ -199,7 +199,7 @@ class MaterialCreator:
class IfcImporter:
- def __init__(self, ifc_import_settings):
+ def __init__(self, ifc_import_settings: IfcImportSettings):
self.ifc_import_settings = ifc_import_settings
self.diff = None
self.file: ifcopenshell.file = None
@@ -725,7 +725,7 @@ class IfcImporter:
if len(subelement.Coordinates) == 3 and self.is_point_far_away(subelement, is_meters=False):
return True
- def apply_blender_offset_to_matrix_world(self, obj, matrix):
+ def apply_blender_offset_to_matrix_world(self, obj: bpy.types.Object, matrix: np.ndarray) -> mathutils.Matrix:
props = bpy.context.scene.BIMGeoreferenceProperties
if props.has_blender_offset:
if obj.data and obj.data.get("has_cartesian_point_offset", None):
@@ -953,7 +953,9 @@ class IfcImporter:
self.create_product(element, mesh=mesh)
def create_products(
- self, products, settings: Optional[ifcopenshell.geom.main.settings] = None
+ self,
+ products: set[ifcopenshell.entity_instance],
+ settings: Optional[ifcopenshell.geom.main.settings] = None,
) -> set[ifcopenshell.entity_instance]:
results = set()
if not products:
@@ -1810,7 +1812,7 @@ class IfcImporter:
if rel.is_a("IfcRelAssignsToGroup") and rel.RelatingGroup.ObjectType == "DRAWING":
return rel.RelatingGroup
- def get_element_matrix(self, element: ifcopenshell.entity_instance) -> np.array:
+ def get_element_matrix(self, element: ifcopenshell.entity_instance) -> np.ndarray:
if isinstance(element, ifcopenshell.sqlite_entity):
result = self.geometry_cache["shapes"][element.id()]["matrix"]
else:
@@ -1959,14 +1961,14 @@ class IfcImporter:
print(traceback.format_exc())
- def a2p(self, o, z, x):
+ def a2p(self, o: mathutils.Vector, z: mathutils.Vector, x: mathutils.Vector) -> mathutils.Matrix:
y = z.cross(x)
r = mathutils.Matrix((x, y, z, o))
r.resize_4x4()
r.transpose()
return r
- def get_axis2placement(self, plc):
+ def get_axis2placement(self, plc: ifcopenshell.entity_instance) -> mathutils.Matrix:
if plc.is_a("IfcAxis2Placement3D"):
z = mathutils.Vector(plc.Axis.DirectionRatios if plc.Axis else (0, 0, 1))
x = mathutils.Vector(plc.RefDirection.DirectionRatios if plc.RefDirection else (1, 0, 0))
@@ -1986,7 +1988,7 @@ class IfcImporter:
o = plc.LocalOrigin.Coordinates
return self.a2p(o, z, x)
- def get_local_placement(self, plc):
+ def get_local_placement(self, plc: Optional[ifcopenshell.entity_instance] = None) -> mathutils.Matrix:
if plc is None:
return mathutils.Matrix()
if plc.PlacementRelTo is None:
@@ -2001,11 +2003,11 @@ class IfcImporter:
bpy.context.scene.BIMRootProperties.contexts = str(subcontext.id())
break
- def link_element(self, element, obj):
+ def link_element(self, element: ifcopenshell.entity_instance, obj: IFC_CONNECTED_TYPE) -> None:
self.added_data[element.id()] = obj
tool.Ifc.link(element, obj)
- def set_matrix_world(self, obj, matrix_world):
+ def set_matrix_world(self, obj: bpy.types.Object, matrix_world: mathutils.Matrix) -> None:
obj.matrix_world = matrix_world
tool.Geometry.record_object_position(obj)
diff --git a/src/blenderbim/blenderbim/tool/geometry.py b/src/blenderbim/blenderbim/tool/geometry.py
index 0a3ff34fdf..7acd2429ed 100644
--- a/src/blenderbim/blenderbim/tool/geometry.py
+++ b/src/blenderbim/blenderbim/tool/geometry.py
@@ -622,7 +622,7 @@ class Geometry(blenderbim.core.tool.Geometry):
obj.data.BIMMeshProperties.material_checksum = str([s.id() for s in cls.get_styles(obj) if s])
@classmethod
- def record_object_position(cls, obj):
+ def record_object_position(cls, obj: bpy.types.Object) -> None:
# These are recorded separately because they have different numerical tolerances
obj.BIMObjectProperties.location_checksum = repr(np.array(obj.matrix_world.translation).tobytes())
obj.BIMObjectProperties.rotation_checksum = repr(np.array(obj.matrix_world.to_3x3()).tobytes())
diff --git a/src/ifcopenshell-python/ifcopenshell/__init__.py b/src/ifcopenshell-python/ifcopenshell/__init__.py
index f23d64d933..fb984b7176 100644
--- a/src/ifcopenshell-python/ifcopenshell/__init__.py
+++ b/src/ifcopenshell-python/ifcopenshell/__init__.py
@@ -39,6 +39,7 @@ import sys
import tempfile
import zipfile
from pathlib import Path
+from typing import Optional
import ifcopenshell.util.file
@@ -197,12 +198,14 @@ def register_schema(schema):
register_schema_attributes(schema.schema)
-def schema_by_name(schema=None, schema_version=None):
+def schema_by_name(
+ schema: Optional[str] = None, schema_version: Optional[tuple[int, ...]] = None
+) -> ifcopenshell_wrapper.schema_definition:
"""Returns an object allowing you to query the IFC schema itself
:param schema: Which IFC schema to use, chosen from "IFC2X3", "IFC4",
or "IFC4X3". These refer to the ISO approved versions of IFC.
- :type schema: string
+ :type schema: string, optional
:param schema_version: If you want to specify an exact version of IFC
that may not be an ISO approved version, use this argument instead
of ``schema``. IFC versions on technical.buildingsmart.org are
@@ -211,7 +214,9 @@ def schema_by_name(schema=None, schema_version=None):
ADD2 TC1, which is the official version approved by ISO when people
refer to "IFC4". Generally you should not use this argument unless
you are testing non-ISO IFC releases.
- :type schema_version: tuple[int]
+ :type schema_version: tuple[int, ...], optional
+ :return: Schema definition object.
+ :rtype: ifocpenshell_wrapper.schema_definition
"""
if schema_version:
prefixes = ("IFC", "X", "_ADD", "_TC")
diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_task_time.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_task_time.py
index 4aed2fd050..68b5d59bbb 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_task_time.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_task_time.py
@@ -17,12 +17,19 @@
# along with IfcOpenShell. If not, see .
import datetime
+import ifcopenshell.util.constraint
import ifcopenshell.util.date
import ifcopenshell.util.sequence
+from typing import Any, Optional
class Usecase:
- def __init__(self, file, task_time=None, attributes=None):
+ def __init__(
+ self,
+ file: ifcopenshell.file,
+ task_time: ifcopenshell.entity_instance,
+ attributes: Optional[dict[str, Any]] = None,
+ ):
"""Edits the attributes of an IfcTaskTime
For more information about the attributes and data types of an
@@ -55,7 +62,7 @@ class Usecase:
self.file = file
self.settings = {"task_time": task_time, "attributes": attributes or {}}
- def execute(self):
+ def execute(self) -> None:
self.task = self.get_task()
self.calendar = ifcopenshell.util.sequence.derive_calendar(self.task)
@@ -169,12 +176,12 @@ class Usecase:
duration, "IfcDuration"
)
- def get_task(self):
- return [
+ def get_task(self) -> ifcopenshell.entity_instance:
+ return next(
e
for e in self.file.get_inverse(self.settings["task_time"])
if e.is_a("IfcTask")
- ][0]
+ )
def handle_resource_calculation(self):
resources = ifcopenshell.util.sequence.get_task_resources(self.task, is_deep=False)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_time.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_time.py
index 76a0521478..4512789fdb 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_time.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_time.py
@@ -17,10 +17,16 @@
# along with IfcOpenShell. If not, see .
import ifcopenshell.util.date
+from typing import Any, Optional
class Usecase:
- def __init__(self, file, work_time=None, attributes=None):
+ def __init__(
+ self,
+ file: ifcopenshell.file,
+ work_time: ifcopenshell.entity_instance,
+ attributes: Optional[dict[str, Any]] = None,
+ ):
"""Edits the attributes of an IfcWorkTime
For more information about the attributes and data types of an
@@ -53,13 +59,15 @@ class Usecase:
self.file = file
self.settings = {"work_time": work_time, "attributes": attributes or {}}
- def execute(self):
+ def execute(self) -> None:
for name, value in self.settings["attributes"].items():
if name in ("Start", "StartDate"):
value = ifcopenshell.util.date.datetime2ifc(value, "IfcDate")
+ # 4 IfcWorktime Start
self.settings["work_time"][4] = value
elif name in ("Finish", "FinishDate"):
value = ifcopenshell.util.date.datetime2ifc(value, "IfcDate")
+ # 5 IfcWorktime Finish
self.settings["work_time"][5] = value
else:
setattr(self.settings["work_time"], name, value)
diff --git a/src/ifcopenshell-python/ifcopenshell/file.py b/src/ifcopenshell-python/ifcopenshell/file.py
index 8c7914a8b0..d5446d731e 100644
--- a/src/ifcopenshell-python/ifcopenshell/file.py
+++ b/src/ifcopenshell-python/ifcopenshell/file.py
@@ -28,7 +28,7 @@ import numbers
import zipfile
import functools
from pathlib import Path
-from typing import List, Optional
+from typing import Optional, Any
import ifcopenshell.util.element
import ifcopenshell.util.file
@@ -52,7 +52,7 @@ class Transaction:
self.batch_delete_ids = set()
self.batch_inverses = []
- def serialise_entity_instance(self, element):
+ def serialise_entity_instance(self, element: ifcopenshell.entity_instance) -> dict[str, Any]:
info = element.get_info()
for key, value in info.items():
info[key] = self.serialise_value(element, value)
@@ -103,7 +103,7 @@ class Transaction:
}
)
- def store_delete(self, element):
+ def store_delete(self, element: ifcopenshell.entity_instance) -> None:
inverses = {}
if self.is_batched:
if element.id() not in self.batch_delete_ids:
diff --git a/src/ifcopenshell-python/ifcopenshell/util/sequence.py b/src/ifcopenshell-python/ifcopenshell/util/sequence.py
index 5b254d973c..db98be456f 100644
--- a/src/ifcopenshell-python/ifcopenshell/util/sequence.py
+++ b/src/ifcopenshell-python/ifcopenshell/util/sequence.py
@@ -20,7 +20,20 @@ import datetime
import ifcopenshell.util.date
from math import floor
from functools import lru_cache
-from collections import namedtuple
+from typing import Union, Literal, Optional, Iterator
+
+
+DURATION_TYPE = Literal["ELAPSEDTIME", "WORKTIME", "NOTDEFINED"]
+RECURRENCE_TYPE = Literal[
+ "BY_DAY_COUNT",
+ "BY_WEEKDAY_COUNT",
+ "DAILY",
+ "MONTHLY_BY_DAY_OF_MONTH",
+ "MONTHLY_BY_POSITION",
+ "WEEKLY",
+ "YEARLY_BY_DAY_OF_MONTH",
+ "YEARLY_BY_POSITION",
+]
def derive_date(task, attribute_name, date=None, is_earliest=False, is_latest=False):
@@ -49,7 +62,7 @@ def derive_date(task, attribute_name, date=None, is_earliest=False, is_latest=Fa
return date
-def derive_calendar(task):
+def derive_calendar(task: ifcopenshell.entity_instance) -> Union[ifcopenshell.entity_instance, None]:
calendar = get_calendar(task)
if calendar:
return calendar
@@ -57,7 +70,7 @@ def derive_calendar(task):
return derive_calendar(rel.RelatingObject)
-def get_calendar(task):
+def get_calendar(task: ifcopenshell.entity_instance) -> Union[ifcopenshell.entity_instance, None]:
calendar = [
rel.RelatingControl
for rel in task.HasAssignments or []
@@ -68,7 +81,7 @@ def get_calendar(task):
return calendar[0]
-def count_working_days(start, finish, calendar):
+def count_working_days(start, finish, calendar: ifcopenshell.entity_instance) -> int:
result = 0
if start == finish:
return 0
@@ -88,7 +101,11 @@ def count_working_days(start, finish, calendar):
def get_start_or_finish_date(
- start, duration, duration_type, calendar, date_type="FINISH"
+ start,
+ duration,
+ duration_type: DURATION_TYPE,
+ calendar: ifcopenshell.entity_instance,
+ date_type: Literal["START", "FINISH"] = "FINISH",
):
if not duration.days:
# Typically a milestone will have zero duration, so the start == finish
@@ -107,7 +124,7 @@ def get_start_or_finish_date(
return datetime.datetime.combine(result, datetime.time(17))
-def offset_date(start, duration, duration_type, calendar):
+def offset_date(start, duration, duration_type: DURATION_TYPE, calendar: ifcopenshell.entity_instance):
current_date = start
months = getattr(duration, "months", 0)
years = getattr(duration, "years", 0)
@@ -129,7 +146,7 @@ def offset_date(start, duration, duration_type, calendar):
return current_date
-def get_soonest_working_day(start, duration_type, calendar):
+def get_soonest_working_day(start, duration_type: DURATION_TYPE, calendar: ifcopenshell.entity_instance):
if duration_type == "ELAPSEDTIME" or not is_calendar_applicable(start, calendar):
return start
while not is_working_day(start, calendar):
@@ -139,7 +156,7 @@ def get_soonest_working_day(start, duration_type, calendar):
return start
-def get_recent_working_day(start, duration_type, calendar):
+def get_recent_working_day(start, duration_type: DURATION_TYPE, calendar: ifcopenshell.entity_instance):
if duration_type == "ELAPSEDTIME" or not is_calendar_applicable(start, calendar):
return start
while not is_working_day(start, calendar):
@@ -150,7 +167,7 @@ def get_recent_working_day(start, duration_type, calendar):
@lru_cache(maxsize=None)
-def is_working_day(day, calendar):
+def is_working_day(day, calendar: ifcopenshell.entity_instance) -> bool:
is_working_day = False
for work_time in calendar.WorkingTimes or []:
if is_work_time_applicable_to_day(work_time, day):
@@ -166,7 +183,7 @@ def is_working_day(day, calendar):
@lru_cache(maxsize=None)
-def is_calendar_applicable(day, calendar):
+def is_calendar_applicable(day, calendar: ifcopenshell.entity_instance) -> bool:
if not calendar or not calendar.WorkingTimes:
return False
is_applicable = False
@@ -177,7 +194,7 @@ def is_calendar_applicable(day, calendar):
return is_applicable
-def is_day_in_work_time(day, work_time):
+def is_day_in_work_time(day, work_time: ifcopenshell.entity_instance) -> bool:
is_day_in_work_time = True
if isinstance(day, datetime.datetime):
day = datetime.date(day.year, day.month, day.day)
@@ -198,7 +215,7 @@ def is_day_in_work_time(day, work_time):
return is_day_in_work_time
-def is_work_time_applicable_to_day(work_time, day):
+def is_work_time_applicable_to_day(work_time: ifcopenshell.entity_instance, day) -> bool:
if not is_day_in_work_time(day, work_time):
return False
if not work_time.RecurrencePattern:
@@ -249,7 +266,7 @@ def is_work_time_applicable_to_day(work_time, day):
return False # TODO
-def get_task_work_schedule(task):
+def get_task_work_schedule(task: ifcopenshell.entity_instance) -> Union[ifcopenshell.entity_instance, None]:
parent_task = get_parent_task(task)
if parent_task:
return get_task_work_schedule(parent_task) or get_task_work_schedule(task)
@@ -262,23 +279,23 @@ def get_task_work_schedule(task):
return None
-def get_nested_tasks(task):
+def get_nested_tasks(task: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]:
return [object for rel in task.IsNestedBy or [] for object in rel.RelatedObjects]
-def get_parent_task(task):
+def get_parent_task(task: ifcopenshell.entity_instance) -> Union[ifcopenshell.entity_instance, None]:
nests = task.Nests
if nests and (obj := nests[0].RelatingObject).is_a("IfcTask"):
return obj
-def get_all_nested_tasks(task):
+def get_all_nested_tasks(task: ifcopenshell.entity_instance) -> Iterator[ifcopenshell.entity_instance]:
for nested_task in get_nested_tasks(task):
yield nested_task
yield from get_all_nested_tasks(nested_task)
-def get_work_schedule_tasks(work_schedule):
+def get_work_schedule_tasks(work_schedule: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]:
tasks = []
for root_task in get_root_tasks(work_schedule):
nested_tasks = get_all_nested_tasks(root_task)
@@ -286,7 +303,7 @@ def get_work_schedule_tasks(work_schedule):
return tasks
-def get_root_tasks(work_schedule):
+def get_root_tasks(work_schedule: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]:
return [
obj
for rel in work_schedule.Controls
@@ -295,7 +312,7 @@ def get_root_tasks(work_schedule):
]
-def get_root_tasks_ids(work_schedule):
+def get_root_tasks_ids(work_schedule: ifcopenshell.entity_instance) -> list[int]:
return [
obj.id()
for rel in work_schedule.Controls
@@ -304,7 +321,7 @@ def get_root_tasks_ids(work_schedule):
]
-def guess_date_range(work_schedule):
+def guess_date_range(work_schedule: ifcopenshell.entity_instance):
earliest = None
latest = None
root_tasks = get_root_tasks(work_schedule)
@@ -326,7 +343,7 @@ def guess_date_range(work_schedule):
return earliest, latest
-def get_direct_task_outputs(task):
+def get_direct_task_outputs(task: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]:
return [
rel.RelatingProduct
for rel in task.HasAssignments
@@ -334,7 +351,7 @@ def get_direct_task_outputs(task):
]
-def get_task_outputs(task, is_deep=False):
+def get_task_outputs(task: ifcopenshell.entity_instance, is_deep=False):
if not is_deep:
return get_direct_task_outputs(task)
else:
@@ -345,7 +362,7 @@ def get_task_outputs(task, is_deep=False):
]
-def get_task_inputs(task, is_deep=False):
+def get_task_inputs(task: ifcopenshell.entity_instance, is_deep=False):
if not is_deep:
return [
object
@@ -368,7 +385,7 @@ def get_task_inputs(task, is_deep=False):
]
-def get_task_resources(task, is_deep=False):
+def get_task_resources(task: ifcopenshell.entity_instance, is_deep=False):
if not is_deep:
return [
object
@@ -391,15 +408,17 @@ def get_task_resources(task, is_deep=False):
]
-def has_task_outputs(task):
+def has_task_outputs(task: ifcopenshell.entity_instance) -> bool:
return len(get_task_outputs(task)) > 0
-def has_task_inputs(task):
+def has_task_inputs(task: ifcopenshell.entity_instance) -> bool:
return len(get_task_inputs(task)) > 0
-def get_tasks_for_product(product, schedule=None):
+def get_tasks_for_product(
+ product: ifcopenshell.entity_instance, schedule: Optional[ifcopenshell.entity_instance] = None
+) -> tuple[list[ifcopenshell.entity_instance], list[ifcopenshell.entity_instance]]:
"""
Get all tasks assigned to or referenced by the given product.
@@ -441,7 +460,7 @@ def get_tasks_for_product(product, schedule=None):
return inputs, outputs
-def get_sequence_assignment(task, sequence="successor"):
+def get_sequence_assignment(task: ifcopenshell.entity_instance, sequence="successor"):
if sequence == "successor":
relationship_attr = "IsPredecessorTo"
elif sequence == "predecessor":