From db31574103685d8035b1a216e52047e4a323c01f Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Tue, 23 Apr 2024 17:05:25 +0500 Subject: [PATCH] typing --- .../api/unit/add_conversion_based_unit.py | 7 +-- .../ifcopenshell/api/unit/add_si_unit.py | 5 ++- .../ifcopenshell/api/unit/assign_unit.py | 22 +++++++--- .../ifcopenshell/api/unit/unassign_unit.py | 4 +- .../ifcopenshell/util/unit.py | 43 ++++++++++--------- .../ifcpatch/recipes/ConvertLengthUnit.py | 8 +++- 6 files changed, 56 insertions(+), 33 deletions(-) diff --git a/src/ifcopenshell-python/ifcopenshell/api/unit/add_conversion_based_unit.py b/src/ifcopenshell-python/ifcopenshell/api/unit/add_conversion_based_unit.py index 8908c9b51f..3102a482fa 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/unit/add_conversion_based_unit.py +++ b/src/ifcopenshell-python/ifcopenshell/api/unit/add_conversion_based_unit.py @@ -18,10 +18,11 @@ import ifcopenshell import ifcopenshell.util.unit +from typing import Optional class Usecase: - def __init__(self, file, name="foot", conversion_offset=None): + def __init__(self, file: ifcopenshell.file, name: str = "foot", conversion_offset: Optional[float] = None): """Add a conversion based unit If you're in one of those countries who don't use SI units, you're @@ -41,7 +42,7 @@ class Usecase: that this is just an example and you don't actually need to specify that for fahrenheit as it's built into this API function. For advanced users only. - :type conversion_offset: float + :type conversion_offset: float, optional :return: The new IfcConversionBasedUnit or IfcConversionBasedUnitWithOffset :rtype: ifcopenshell.entity_instance.entity_instance @@ -60,7 +61,7 @@ class Usecase: self.file = file self.settings = {"name": name, "conversion_offset": conversion_offset} - def execute(self): + def execute(self) -> ifcopenshell.entity_instance: unit_type = ifcopenshell.util.unit.imperial_types.get(self.settings["name"], "USERDEFINED") dimensions = ifcopenshell.util.unit.named_dimensions[unit_type] exponents = self.file.createIfcDimensionalExponents(*dimensions) diff --git a/src/ifcopenshell-python/ifcopenshell/api/unit/add_si_unit.py b/src/ifcopenshell-python/ifcopenshell/api/unit/add_si_unit.py index 82a7b5f66a..bd0cd26e39 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/unit/add_si_unit.py +++ b/src/ifcopenshell-python/ifcopenshell/api/unit/add_si_unit.py @@ -17,10 +17,11 @@ # along with IfcOpenShell. If not, see . import ifcopenshell.util.unit +from typing import Optional class Usecase: - def __init__(self, file, unit_type="LENGTHUNIT", prefix=None): + def __init__(self, file: ifcopenshell.file, unit_type: str = "LENGTHUNIT", prefix: Optional[str] = None): """Add a new SI unit The supported types are ABSORBEDDOSEUNIT, AMOUNTOFSUBSTANCEUNIT, @@ -59,7 +60,7 @@ class Usecase: self.file = file self.settings = {"unit_type": unit_type, "prefix": prefix} - def execute(self): + def execute(self) -> ifcopenshell.entity_instance: name = ifcopenshell.util.unit.si_type_names.get(self.settings["unit_type"], None) return self.file.create_entity( "IfcSIUnit", UnitType=self.settings["unit_type"], Name=name, Prefix=self.settings["prefix"] diff --git a/src/ifcopenshell-python/ifcopenshell/api/unit/assign_unit.py b/src/ifcopenshell-python/ifcopenshell/api/unit/assign_unit.py index 779dc6826d..bca68744d7 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/unit/assign_unit.py +++ b/src/ifcopenshell-python/ifcopenshell/api/unit/assign_unit.py @@ -18,10 +18,18 @@ import ifcopenshell import ifcopenshell.util.unit +from typing import Optional class Usecase: - def __init__(self, file, units=None, length=None, area=None, volume=None): + def __init__( + self, + file: ifcopenshell.file, + units: Optional[list[ifcopenshell.entity_instance]] = None, + length: Optional[dict] = None, + area: Optional[dict] = None, + volume: Optional[dict] = None, + ): """Assign default project units Whenever a unitised quantity is specified, such as a length, area, @@ -67,7 +75,7 @@ class Usecase: self.settings["area"] = area or {"is_metric": True, "raw": "METERS"} self.settings["volume"] = volume or {"is_metric": True, "raw": "METERS"} - def execute(self): + def execute(self) -> ifcopenshell.entity_instance: # We're going to refactor this to split unit creation and assignment if self.settings["units"]: units = self.settings["units"] @@ -84,7 +92,7 @@ class Usecase: self.assign_units(unit_assignment, units) return unit_assignment - def get_unit_assignment(self): + def get_unit_assignment(self) -> ifcopenshell.entity_instance: unit_assignment = self.file.by_type("IfcUnitAssignment") if unit_assignment: unit_assignment = unit_assignment[0] @@ -97,13 +105,15 @@ class Usecase: self.file.by_type("IfcContext")[0].UnitsInContext = unit_assignment return unit_assignment - def assign_units(self, unit_assignment, new_units): + def assign_units( + self, unit_assignment: ifcopenshell.entity_instance, new_units: list[ifcopenshell.entity_instance] + ) -> None: units = set(unit_assignment.Units or []) for unit in new_units: units.add(unit) unit_assignment.Units = list(units) - def create_metric_unit(self, unit_type, data): + def create_metric_unit(self, unit_type: str, data: dict) -> ifcopenshell.entity_instance: type_prefix = "" if unit_type == "area": type_prefix = "SQUARE_" @@ -116,7 +126,7 @@ class Usecase: type_prefix + ifcopenshell.util.unit.get_unit_name(data["raw"]), ) - def create_imperial_unit(self, unit_type, data): + def create_imperial_unit(self, unit_type: str, data: dict) -> ifcopenshell.entity_instance: if unit_type == "length": dimensional_exponents = self.file.createIfcDimensionalExponents(1, 0, 0, 0, 0, 0, 0) name_prefix = "" diff --git a/src/ifcopenshell-python/ifcopenshell/api/unit/unassign_unit.py b/src/ifcopenshell-python/ifcopenshell/api/unit/unassign_unit.py index 0876921d84..fb1dcafb02 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/unit/unassign_unit.py +++ b/src/ifcopenshell-python/ifcopenshell/api/unit/unassign_unit.py @@ -15,10 +15,12 @@ # # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . +import ifcopenshell +from typing import Optional class Usecase: - def __init__(self, file, units=None): + def __init__(self, file: ifcopenshell.file, units: Optional[list[ifcopenshell.entity_instance]] = None): """Unassigns units as default units for the project :param units: A list of units to assign as project defaults. diff --git a/src/ifcopenshell-python/ifcopenshell/util/unit.py b/src/ifcopenshell-python/ifcopenshell/util/unit.py index 102b7b0d61..8b2ec3c167 100644 --- a/src/ifcopenshell-python/ifcopenshell/util/unit.py +++ b/src/ifcopenshell-python/ifcopenshell/util/unit.py @@ -21,6 +21,7 @@ from math import pi from typing import Iterable, Any, Union, Literal, Optional import ifcopenshell +import ifcopenshell.ifcopenshell_wrapper as ifcopenshell_wrapper import ifcopenshell.api prefixes = { @@ -353,7 +354,7 @@ def get_prefix_multiplier(text): return 1 -def get_unit_name(text): +def get_unit_name(text: str) -> Union[str, None]: text = text.upper().replace("METER", "METRE") for name in unit_names: if name.replace("_", " ") in text: @@ -368,13 +369,13 @@ def get_named_dimensions(name): return named_dimensions.get(name, (0, 0, 0, 0, 0, 0, 0)) -def get_unit_assignment(ifc_file): +def get_unit_assignment(ifc_file: ifcopenshell.file) -> Union[ifcopenshell.entity_instance, None]: unit_assignments = ifc_file.by_type("IfcUnitAssignment") if unit_assignments: return unit_assignments[0] -def get_project_unit(ifc_file, unit_type): +def get_project_unit(ifc_file: ifcopenshell.file, unit_type: str) -> Union[ifcopenshell.entity_instance, None]: """Get the default project unit of a particular unit type :param ifc_file: The IFC file. @@ -384,7 +385,7 @@ def get_project_unit(ifc_file, unit_type): :type unit_type: str :return: The IFC unit entity, or nothing if there is no default project unit defined. - :rtype: ifcopenshell.entity_instance,None + :rtype: Union[ifcopenshell.entity_instance, None] """ unit_assignment = get_unit_assignment(ifc_file) if unit_assignment: @@ -393,7 +394,9 @@ def get_project_unit(ifc_file, unit_type): return unit -def get_property_unit(prop, ifc_file): +def get_property_unit( + prop: ifcopenshell.entity_instance, ifc_file: ifcopenshell.file +) -> Union[ifcopenshell.entity_instance, None]: unit = getattr(prop, "Unit", None) if unit: return unit @@ -446,14 +449,14 @@ def get_property_unit(prop, ifc_file): return units[0] -def get_unit_measure_class(unit_type): +def get_unit_measure_class(unit_type: str) -> str: if unit_type == "USERDEFINED": # See https://github.com/buildingSMART/IFC4.3.x-development/issues/71 return "IfcNumericMeasure" return "Ifc" + unit_type[0:-4].lower().capitalize() + "Measure" -def get_measure_unit_type(measure_class): +def get_measure_unit_type(measure_class: str) -> str: if measure_class == "IfcNumericMeasure": # See https://github.com/buildingSMART/IFC4.3.x-development/issues/71 return "USERDEFINED" @@ -462,7 +465,7 @@ def get_measure_unit_type(measure_class): return measure_class.upper() + "UNIT" -def get_symbol_measure_class(symbol): +def get_symbol_measure_class(symbol: Optional[str] = None) -> str: # Dumb, but everybody gets it, unlike regex golf if not symbol: return "IfcNumericMeasure" @@ -480,7 +483,7 @@ def get_symbol_measure_class(symbol): return "IfcNumericMeasure" -def get_symbol_quantity_class(symbol): +def get_symbol_quantity_class(symbol: Optional[str] = None) -> str: # Dumb, but everybody gets it, unlike regex golf if not symbol: return "IfcQuantityCount" @@ -498,7 +501,7 @@ def get_symbol_quantity_class(symbol): return "IfcQuantityCount" -def get_unit_symbol(unit): +def get_unit_symbol(unit: ifcopenshell.entity_instance) -> str: symbol = "" if unit.is_a("IfcSIUnit"): symbol += prefix_symbols.get(unit.Prefix, "") @@ -508,7 +511,7 @@ def get_unit_symbol(unit): return symbol -def convert_unit(value, from_unit, to_unit): +def convert_unit(value: float, from_unit: ifcopenshell.entity_instance, to_unit: ifcopenshell.entity_instance) -> float: """Convert from one unit to another unit :param value: The numeric value you want to convert @@ -668,9 +671,9 @@ def format_length( def is_attr_type( - content_type: Union[ifcopenshell.ifcopenshell_wrapper.named_type, ifcopenshell.ifcopenshell_wrapper.type_declaration], + content_type: ifcopenshell_wrapper.parameter_type, ifc_unit_type_name: str, -) -> Union[ifcopenshell.ifcopenshell_wrapper.type_declaration, None]: +) -> Union[ifcopenshell_wrapper.type_declaration, None]: cur_decl = content_type while hasattr(cur_decl, "declared_type") is True: cur_decl = cur_decl.declared_type() @@ -679,7 +682,7 @@ def is_attr_type( if cur_decl.name() == ifc_unit_type_name: return cur_decl - if isinstance(cur_decl, ifcopenshell.ifcopenshell_wrapper.aggregation_type): + if isinstance(cur_decl, ifcopenshell_wrapper.aggregation_type): res = cur_decl.type_of_element() cur_decl = res.declared_type() if hasattr(cur_decl, "name") and cur_decl.name() == ifc_unit_type_name: @@ -696,13 +699,13 @@ def is_attr_type( def iter_element_and_attributes_per_type( ifc_file: ifcopenshell.file, attr_type_name: str -) -> Iterable[tuple[ifcopenshell.entity_instance, ifcopenshell.ifcopenshell_wrapper.attribute, Any, str]]: - schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(ifc_file.schema) +) -> Iterable[tuple[ifcopenshell.entity_instance, ifcopenshell_wrapper.attribute, Any]]: + schema: ifcopenshell_wrapper.schema_definition = ifcopenshell_wrapper.schema_by_name(ifc_file.schema) for element in ifc_file: entity = schema.declaration_by_name(element.is_a()) attrs = entity.all_attributes() - for i, (attr, val, is_derived) in enumerate(zip(attrs, list(element), entity.derived())): + for attr, val, is_derived in zip(attrs, list(element), entity.derived()): if is_derived: continue @@ -725,7 +728,7 @@ def convert_file_length_units(ifc_file: ifcopenshell.file, target_units: str) -> # Copy all elements from the original file to the patched file file_patched = ifcopenshell.file.from_string(ifc_file.wrapped_data.to_string()) - unit_assignment = ifcopenshell.util.unit.get_unit_assignment(file_patched) + unit_assignment = get_unit_assignment(file_patched) old_length = [u for u in unit_assignment.Units if getattr(u, "UnitType", None) == "LENGTHUNIT"][0] new_length = ifcopenshell.api.run("unit.add_si_unit", file_patched, unit_type="LENGTHUNIT", prefix=prefix) @@ -733,10 +736,10 @@ def convert_file_length_units(ifc_file: ifcopenshell.file, target_units: str) -> # Traverse all elements and their nested attributes in the file and convert them for element, attr, val in iter_element_and_attributes_per_type(file_patched, "IfcLengthMeasure"): if isinstance(val, tuple): - new_value = [ifcopenshell.util.unit.convert_unit(v, old_length, new_length) for v in val] + new_value = [convert_unit(v, old_length, new_length) for v in val] setattr(element, attr.name(), tuple(new_value)) else: - new_value = ifcopenshell.util.unit.convert_unit(val, old_length, new_length) + new_value = convert_unit(val, old_length, new_length) setattr(element, attr.name(), new_value) file_patched.remove(old_length) diff --git a/src/ifcpatch/ifcpatch/recipes/ConvertLengthUnit.py b/src/ifcpatch/ifcpatch/recipes/ConvertLengthUnit.py index 7483569122..e50bbed478 100644 --- a/src/ifcpatch/ifcpatch/recipes/ConvertLengthUnit.py +++ b/src/ifcpatch/ifcpatch/recipes/ConvertLengthUnit.py @@ -26,7 +26,13 @@ from logging import Logger class Patcher: - def __init__(self, src: str, file: ifcopenshell.file, logger: Logger, unit: str = "METERS"): + def __init__( + self, + src: str, + file: ifcopenshell.file, + logger: Logger, + unit: str = "METERS", + ): """Converts the length unit of a model to the specified unit Allowed metric units include METERS, MILLIMETERS, CENTIMETERS, etc.