diff --git a/TODO.md b/TODO.md index d12b276..f475c51 100644 --- a/TODO.md +++ b/TODO.md @@ -510,7 +510,7 @@ Header color style can't be done, unfortunately. Body color feels unclean, so no ## BLCache - [ ] Replace every raw property with `BLField`. -- [ ] Add matrix property support: https://developer.blender.org/docs/release_notes/3.0/python_api/#other-additions +- [x] Add matrix property support: https://developer.blender.org/docs/release_notes/3.0/python_api/#other-additions - [ ] Fix many problems by persisting `_enum_cb_cache` and `_str_cb_cache`. - [ ] Docstring parser for descriptions. - [ ] Method of dynamically setting property options after creation, using `idproperty_ui_data` @@ -524,6 +524,10 @@ We're trying to do our part by reporting bugs we find! This is where we keep track of them for now, if they're not covered by the above listings. ## Blender Maxwell Bugs +See Issues. + +## Testing +- [ ] `pytest` integration exhibits a bootstrapping problem when using https://github.com/mondeja/pytest-blender ## Blender Bugs Reported: @@ -545,6 +549,7 @@ Unreported: ## Tidy3D bugs Unreported: - Directly running `SimulationTask.get()` is missing fields - it doesn't return some fields, including `created_at`. Listing tasks by folder is not broken. +- Frequency ranges don't check for repeated elements. diff --git a/pyproject.toml b/pyproject.toml index 852ba57..6c83e46 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -39,7 +39,8 @@ managed = true virtual = true dev-dependencies = [ "ruff>=0.3.2", - "fake-bpy-module-4-0>=20231118", ## TODO: Update to Blender 4.1.0 + "fake-bpy-module-4-0>=20231118", + ## TODO: Update to Blender 4.1.0 ] [tool.rye.scripts] @@ -143,3 +144,9 @@ max-args = 6 quote-style = "single" indent-style = "tab" docstring-code-format = false + +#################### +# - Tooling: Pytest +#################### +#[tool.pytest.ini_options] + diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/flow_kinds/__init__.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/flow_kinds/__init__.py index b85eb2e..48834bc 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/flow_kinds/__init__.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/flow_kinds/__init__.py @@ -1,17 +1,19 @@ from .array import ArrayFlow -from .capabiltiies import CapabilitiesFlow +from .capabilities import CapabilitiesFlow from .flow_kinds import FlowKind -from .lazy_array_range import LazyArrayRange -from .lazy_value_func import LazyValueFunc -from .params import Params +from .info import InfoFlow +from .lazy_array_range import LazyArrayRangeFlow +from .lazy_value_func import LazyValueFuncFlow +from .params import ParamsFlow from .value import ValueFlow __all__ = [ 'ArrayFlow', 'CapabilitiesFlow', 'FlowKind', - 'LazyArrayRange', - 'LazyValueFunc', - 'Params', + 'InfoFlow', + 'LazyArrayRangeFlow', + 'LazyValueFuncFlow', + 'ParamsFlow', 'ValueFlow', ] diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/flow_kinds/flow_kinds.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/flow_kinds/flow_kinds.py index fd00f59..51c0998 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/flow_kinds/flow_kinds.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/flow_kinds/flow_kinds.py @@ -50,19 +50,21 @@ class FlowKind(enum.StrEnum): def scale_to_unit_system( cls, kind: typ.Self, - value, + flow_obj, unit_system: spux.UnitSystem, ): - if kind == cls.Value: + log.debug('%s: Scaling "%s" to Unit System', kind, str(flow_obj)) + if kind == FlowKind.Value: return spux.scale_to_unit_system( - value, + flow_obj, unit_system, ) - if kind == cls.LazyArrayRange: - return value.rescale_to_unit_system(unit_system) + if kind == FlowKind.LazyArrayRange: + log.debug([kind, flow_obj, unit_system]) + return flow_obj.rescale_to_unit_system(unit_system) - if kind == cls.Params: - return value.rescale_to_unit_system(unit_system) + if kind == FlowKind.Params: + return flow_obj.rescale_to_unit_system(unit_system) msg = 'Tried to scale unknown kind' raise ValueError(msg) diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/flow_kinds/lazy_array_range.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/flow_kinds/lazy_array_range.py index e5d03e1..7792a38 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/flow_kinds/lazy_array_range.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/flow_kinds/lazy_array_range.py @@ -8,11 +8,14 @@ import jaxtyping as jtyp import sympy as sp from blender_maxwell.utils import extra_sympy_units as spux +from blender_maxwell.utils import logger from .array import ArrayFlow from .flow_kinds import FlowKind from .lazy_value_func import LazyValueFuncFlow +log = logger.get(__name__) + @dataclasses.dataclass(frozen=True, kw_only=True) class LazyArrayRangeFlow: @@ -84,6 +87,16 @@ class LazyArrayRangeFlow: @functools.cached_property def mathtype(self) -> spux.MathType: + """Conservatively compute the most stringent `spux.MathType` that can represent both `self.start` and `self.stop`. + + Notes: + The mathtype is determined from start/stop either using `sympy` assumptions, or as Python types. + + For precise information on how start/stop are "combined", see `spux.MathType.combine()`. + + Returns: + All symbols valid for use in the expression. + """ # Get Start Mathtype if isinstance(self.start, spux.SympyType): start_mathtype = spux.MathType.from_expr(self.start) @@ -97,12 +110,22 @@ class LazyArrayRangeFlow: stop_mathtype = spux.MathType.from_pytype(type(self.stop)) # Check Equal - if start_mathtype != stop_mathtype: - return spux.MathType.combine(start_mathtype, stop_mathtype) - - return start_mathtype + combined_mathtype = spux.MathType.combine(start_mathtype, stop_mathtype) + log.debug( + '%s: Computed MathType as %s (start_mathtype=%s, stop_mathtype=%s)', + self, + combined_mathtype, + start_mathtype, + stop_mathtype, + ) + return combined_mathtype def __len__(self): + """Compute the length of the array to be realized. + + Returns: + The number of steps. + """ return self.steps #################### @@ -121,6 +144,11 @@ class LazyArrayRangeFlow: ValueError: If the existing unit is `None`, indicating that there is no unit to correct. """ if self.unit is not None: + log.debug( + '%s: Corrected unit to %s', + self, + corrected_unit, + ) return LazyArrayRangeFlow( start=self.start, stop=self.stop, @@ -146,6 +174,11 @@ class LazyArrayRangeFlow: ValueError: If the existing unit is `None`, indicating that there is no unit to correct. """ if self.unit is not None: + log.debug( + '%s: Scaled to unit %s', + self, + unit, + ) return LazyArrayRangeFlow( start=spux.scale_to_unit(self.start * self.unit, unit), stop=spux.scale_to_unit(self.stop * self.unit, unit), @@ -171,13 +204,18 @@ class LazyArrayRangeFlow: ValueError: If the existing unit is `None`, indicating that there is no unit to correct. """ if self.unit is not None: + log.debug( + '%s: Scaled to unit system: %s', + self, + str(unit_system), + ) return LazyArrayRangeFlow( start=spux.strip_unit_system( spux.convert_to_unit_system(self.start * self.unit, unit_system), unit_system, ), stop=spux.strip_unit_system( - spux.convert_to_unit_system(self.start * self.unit, unit_system), + spux.convert_to_unit_system(self.stop * self.unit, unit_system), unit_system, ), steps=self.steps, diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/outputs/web_exporters/tidy3d_web_exporter.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/outputs/web_exporters/tidy3d_web_exporter.py index dc387c0..458c8dc 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/outputs/web_exporters/tidy3d_web_exporter.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/outputs/web_exporters/tidy3d_web_exporter.py @@ -320,7 +320,7 @@ class Tidy3DWebExporterNode(base.MaxwellSimNode): def on_sim_changed(self, props) -> None: # Sim Linked | First Value Change if self.inputs['Sim'].is_linked and not props['sim_info_available']: - log.critical('First Change: Mark Sim Info Available') + log.debug('%s: First Change; Mark Sim Info Available', self.sim_node_name) self.sim = bl_cache.Signal.InvalidateCache self.total_monitor_data = bl_cache.Signal.InvalidateCache self.is_sim_uploadable = bl_cache.Signal.InvalidateCache @@ -332,7 +332,7 @@ class Tidy3DWebExporterNode(base.MaxwellSimNode): and props['sim_info_available'] and not props['sim_info_invalidated'] ): - log.critical('Second Change: Mark Sim Info Invalided') + log.debug('%s: Second Change; Mark Sim Info Invalided', self.sim_node_name) self.sim_info_invalidated = True # Sim Linked | Nth Time @@ -346,7 +346,9 @@ class Tidy3DWebExporterNode(base.MaxwellSimNode): ## -> Luckily, since we know there's no sim, invalidation is cheap. ## -> Ends up being a "circuit breaker" for sim_info_invalidated. elif not self.inputs['Sim'].is_linked: - log.critical('Unlinked: Short Circuit Zap Cache') + log.debug( + '%s: Unlinked; Short Circuit the Sim Info Cache', self.sim_node_name + ) self.sim = bl_cache.Signal.InvalidateCache self.total_monitor_data = bl_cache.Signal.InvalidateCache self.is_sim_uploadable = bl_cache.Signal.InvalidateCache @@ -368,7 +370,7 @@ class Tidy3DWebExporterNode(base.MaxwellSimNode): props={'uploaded_task_id'}, ) def on_uploaded_task_changed(self, props): - log.critical('Uploaded Task Changed') + log.debug('Uploaded Task Changed') self.is_sim_uploadable = bl_cache.Signal.InvalidateCache if props['uploaded_task_id'] != '': diff --git a/src/blender_maxwell/services/tdcloud.py b/src/blender_maxwell/services/tdcloud.py index 7226a83..cfe6fb0 100644 --- a/src/blender_maxwell/services/tdcloud.py +++ b/src/blender_maxwell/services/tdcloud.py @@ -49,15 +49,19 @@ def set_offline(): def check_online() -> bool: global IS_ONLINE # noqa: PLW0603 + log.info('Checking Internet Connection...') + try: urllib.request.urlopen( 'https://docs.flexcompute.com/projects/tidy3d/en/latest/index.html', timeout=2, ) - except: + except: # noqa: E722 + log.info('Internet is currently offline') IS_ONLINE = False return False else: + log.info('Internet connection is working') IS_ONLINE = True return True @@ -67,26 +71,25 @@ def check_online() -> bool: #################### def check_authentication() -> bool: global IS_AUTHENTICATED # noqa: PLW0603 - log.critical('Checking Authentication') - - # Check Previous Authentication - ## If we authenticated once, we presume that it'll work again. - ## TODO: API keys can change... It would just look like "offline" for now. - if IS_AUTHENTICATED: - return True + log.info('Checking Tidy3D Authentication...') api_key = td_web.core.http_util.api_key() if api_key is not None: + log.info('Found stored Tidy3D API key') try: td_web.test() - set_online() except td.exceptions.WebError: set_offline() + log.info('Authenticated to Tidy3D cloud') return False + else: + set_online() + log.info('Authenticated to Tidy3D cloud') IS_AUTHENTICATED = True return True + log.info('Tidy3D API key is missing') return False @@ -99,6 +102,7 @@ TD_CONFIG = Path(td_web.cli.constants.CONFIG_FILE) ## TODO: Robustness is key - internet might be down. ## -> I'm not a huge fan of the max 2sec startup time burden +## -> I also don't love "calling" Tidy3D on startup, privacy-wise if TD_CONFIG.is_file() and check_online(): check_authentication() diff --git a/src/scripts/dev.py b/src/scripts/dev.py index dcc0390..d3d40ef 100644 --- a/src/scripts/dev.py +++ b/src/scripts/dev.py @@ -83,7 +83,6 @@ if __name__ == '__main__': # Run Addon print(f'Blender: Running "{info.ADDON_NAME}"...') - subprocess.run return_code, output = run_blender( None, headless=False, load_devfile=True, monitor=True )