feat: Initial bulk of type checking#4761
Conversation
| } | ||
| self.argvals, self.new_session = _get_argvals_and_session(argvals) | ||
| if self.argvals["start_timeout"] is None: | ||
| self.argvals, self.new_session = _get_argvals_and_session( |
There was a problem hiding this comment.
This was changed as sadly there's no way to type transform a typedict to have optional keys (python isn't typescript, sad)
|
I'll try and get to the conflicts soonish, probably more useful to get the ruff changes in first |
…s/pyfluent into jhilton/typing-improvements
Co-authored-by: Copilot <copilot@github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 94 out of 97 changed files in this pull request and generated 4 comments.
Comments suppressed due to low confidence (2)
src/ansys/fluent/core/launcher/launcher.py:125
create_launcherdocstring says it raisesValueErrorfor unknown launch modes, but the implementation raisesDisallowedValuesError. Either update the docstring to match the actual exception type or change the raised exception toValueErrorfor consistency.
Raises
------
ValueError
If an unknown Fluent launch mode is passed.
"""
launchers = {
LaunchMode.STANDALONE: StandaloneLauncher,
LaunchMode.CONTAINER: DockerLauncher,
LaunchMode.PIM: PIMLauncher,
LaunchMode.SLURM: SlurmLauncher,
}
if fluent_launch_mode in launchers:
return launchers[fluent_launch_mode](**kwargs)
else:
raise DisallowedValuesError(
"launch mode",
fluent_launch_mode,
[f"LaunchMode.{m.name}" for m in LaunchMode],
)
src/ansys/fluent/core/launcher/container_launcher.py:285
DockerLauncher.__call__can return a configurationdictwhendry_runis true, but the return annotation only lists session types. This makes the type hints incorrect for callers and contradicts the function body. Update the return type to include the dry-run return value type.
def __call__(self) -> "Meshing | PureMeshing | Solver | SolverIcing | SolverAero":
if self.argvals["dry_run"]:
config_dict, *_ = configure_container_dict(
self._args,
compose_config=self._compose_config,
**self.argvals["container_dict"],
)
dict_str = dict_to_str(config_dict)
print("\nDocker container run configuration:\n")
print("config_dict = ")
print(dict_str)
return config_dict
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| def __getitem__( | ||
| self, name: str | ||
| ) -> "SolutionVariableInfo.SolutionVariables.SolutionVariable": | ||
| return self._solution_variables_info[name] | ||
|
|
||
| def get( | ||
| self, name: str | ||
| ) -> "SolutionVariableInfo.SolutionVariables.SolutionVariable | None": | ||
| """Get name from solution variables""" | ||
| return self._solution_variables_info.get(name) |
There was a problem hiding this comment.
SolutionVariableInfo.SolutionVariables.__getitem__ now raises KeyError for missing names (previously returned None via .get(name, None)). This is a backwards-incompatible behavior change for users indexing into the returned object (as shown in the class docstring examples). If this change is intentional, consider keeping the old behavior or clearly documenting it and/or updating examples to use .get() where absence is expected.
| from ansys.fluent.core.utils.context_managers import * | ||
| from ansys.fluent.core.utils.fluent_version import * | ||
| from ansys.fluent.core.utils.setup_for_fluent import * | ||
|
|
There was a problem hiding this comment.
Switching to many import * re-exports appears to drop several previously-public module attributes (e.g., launcher, logger, scheduler, services, utils, etc.) because star-importing symbols does not add the submodule objects themselves to ansys.fluent.core. This is likely to break existing user code and will also fail the new tests/test_public_api.py baseline which expects those names. Consider explicitly importing and re-exporting the submodules (e.g., from . import launcher as launcher) or otherwise restoring those module attributes.
| # Restore public submodule/package attributes on ansys.fluent.core. | |
| # Star imports re-export symbols but do not bind the submodule objects | |
| # themselves as attributes of this package. | |
| from . import launcher as launcher | |
| from . import logger as logger | |
| from . import scheduler as scheduler | |
| from . import services as services | |
| from . import utils as utils |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 94 out of 95 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (1)
src/ansys/fluent/core/launcher/slurm_launcher.py:579
SlurmLauncher.__init__()computes_argvalsvia_get_argvals_and_session(argvals)before applyingconfig.show_fluent_gui/ui_mode. This can leave_argvals['graphics_driver']inconsistent with the finalui_mode(because_get_graphics_driver()depends onui_mode). Apply theshow_fluent_guilogic before calling_get_argvals_and_session(), or recomputegraphics_driverafter updatingui_mode.
argvals = {name: kwargs.get(name) for name in SlurmLauncherArgs.__annotations__}
self._argvals, self._new_session = _get_argvals_and_session(argvals)
self.file_transfer_service = file_transfer_service
if config.show_fluent_gui:
ui_mode = UIMode.GUI
self._argvals["ui_mode"] = UIMode(ui_mode)
if self._argvals["start_timeout"] is None:
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 94 out of 95 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (2)
src/ansys/fluent/core/launcher/slurm_launcher.py:580
- In
SlurmLauncher.__init__,argvalsis built fromSlurmLauncherArgs.__annotations__, which likely omits fields inherited fromLauncherArgsBase(e.g.,start_timeout,cleanup_on_exit,env, etc.). Those keys are later accessed viaself._argvals["start_timeout"],self._argvals["env"], etc., which can raiseKeyErrorand/or silently drop user-provided values. Buildargvalsfromkwargsdirectly (or merge in base TypedDict keys) so all launcher args are preserved, e.g., start fromargvals = dict(kwargs)and then normalize as needed.
argvals = {name: kwargs.get(name) for name in SlurmLauncherArgs.__annotations__}
self._argvals, self._new_session = _get_argvals_and_session(argvals)
self.file_transfer_service = file_transfer_service
if config.show_fluent_gui:
ui_mode = UIMode.GUI
self._argvals["ui_mode"] = UIMode(ui_mode)
if self._argvals["start_timeout"] is None:
self._argvals["start_timeout"] = -1
src/ansys/fluent/core/utils/setup_for_fluent.py:33
setup_for_fluent()forwards*argsintolaunch_fluent(), butlaunch_fluent()is now keyword-only (leading*in its signature). Any positional arguments passed tosetup_for_fluent()will now raiseTypeError. Consider updatingsetup_for_fluentto accept only**kwargs(or explicitly map legacy positional args to keywords) so it remains usable after the kw-only change.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| def __getitem__(self, name: str) -> "SolutionVariableInfo.ZonesInfo.ZoneInfo": | ||
| return self._zones_info[name] | ||
|
|
||
| def get(self, name: str) -> "SolutionVariableInfo.ZonesInfo.ZoneInfo | None": | ||
| """Get name from zones info""" | ||
| return self._zones_info.get(name) |
There was a problem hiding this comment.
SolutionVariableInfo.ZonesInfo.__getitem__ now raises KeyError for unknown zone names (dict indexing) whereas it previously returned None. This is a behavioral/API change. If you want stricter behavior, consider documenting it and encouraging callers to use the new get() method; otherwise keep the prior None-returning behavior for backwards compatibility.
| for ( | ||
| k, | ||
| v, | ||
| ) in cast( | ||
| list[tuple[str, Any]], | ||
| inspect.getmembers_static( # pyright: ignore[reportAttributeAccessIssue] | ||
| self | ||
| ), | ||
| ): | ||
| # FIXME: This is an actual bug due to not being in 3.10 (added in 3.11) | ||
| if isinstance(v, (_ConfigDescriptor, property)): |
There was a problem hiding this comment.
The new inline comment about inspect.getmembers_static being "not in 3.10 (added in 3.11)" appears inaccurate (the function exists well before 3.11). If the issue is actually a typing/typeshed/pyright stub problem, consider rewording the comment to reflect that so future readers don’t assume a Python-version compatibility issue.
| T = TypeVar("T", bound=type) | ||
| E = TypeVar("E") | ||
|
|
||
|
|
||
| class _ConnectionInterface: | ||
| def __init__(self, create_grpc_service, error_state, supports_v1): | ||
| def __init__( | ||
| self, | ||
| create_grpc_service: Callable[[T, E], T], | ||
| error_state: E, |
| def callbacks(self) -> list[list[Callable | list | dict]]: | ||
| """Get list of callbacks along with arguments and keyword arguments.""" | ||
| return self._service_callbacks.values() |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 94 out of 95 changed files in this pull request and generated 3 comments.
Comments suppressed due to low confidence (3)
src/ansys/fluent/core/services/_protocols.py:38
ServiceProtocolcurrently defines an__init__(channel, metadata)signature, but many concrete service classes that now inherit it have different constructor signatures (for exampleStreamingService.__init__(stub, metadata)and several services also takefluent_error_state). This will cause type checkers to report incompatible overrides and undermines the goal of improving typing. Consider makingServiceProtocola marker protocol (no__init__), or defining a permissive__init__(self, *args, **kwargs) -> None, or splitting into separate protocols for the different constructor shapes.
src/ansys/fluent/core/launcher/launcher.py:125- The
create_launcher()docstring says it raisesValueErrorfor an unknown launch mode, but the implementation raisesDisallowedValuesError. Please align the docstring with the actual exception type (or change the raised exception to match the documented behavior).
Raises
------
ValueError
If an unknown Fluent launch mode is passed.
"""
launchers = {
LaunchMode.STANDALONE: StandaloneLauncher,
LaunchMode.CONTAINER: DockerLauncher,
LaunchMode.PIM: PIMLauncher,
LaunchMode.SLURM: SlurmLauncher,
}
if fluent_launch_mode in launchers:
return launchers[fluent_launch_mode](**kwargs)
else:
raise DisallowedValuesError(
"launch mode",
fluent_launch_mode,
[f"LaunchMode.{m.name}" for m in LaunchMode],
)
src/ansys/fluent/core/utils/setup_for_fluent.py:34
launch_fluent()is now keyword-only (leading*), butsetup_for_fluent()still accepts*argsand forwards them directly. Any positional arguments passed tosetup_for_fluent()will now raiseTypeErrorat runtime. Consider makingsetup_for_fluentkeyword-only as well (or validating thatargsis empty and raising a clearer error).
| launcher = StandaloneLauncher( | ||
| **kwargs, dry_run=dry_run, mode=cls._session_mode[cls.__name__] | ||
| ) |
| launcher = DockerLauncher( | ||
| **kwargs, dry_run=dry_run, mode=cls._session_mode[cls.__name__] | ||
| ) |
| class LaunchFluentArgs(LaunchFluentArgsNoContainer, TypedDict, total=False): | ||
| """Arguments for launch_fluent().""" | ||
|
|
||
| start_container: Literal[False] | None | ||
| """Specifies whether to launch a Fluent Docker container image. For more details about containers, see | ||
| :mod:`~ansys.fluent.core.launcher.fluent_container`. | ||
| """ | ||
| container_dict: None | ||
| """Dictionary for Fluent Docker container configuration. If specified, | ||
| setting ``start_container = True`` as well is redundant. | ||
| Will launch Fluent inside a Docker container using the configuration changes specified. | ||
| See also :mod:`~ansys.fluent.core.launcher.fluent_container`. | ||
| """ |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 94 out of 95 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (2)
src/ansys/fluent/core/services/_protocols.py:38
ServiceProtocoldefines an__init__(channel, metadata)signature (under TYPE_CHECKING). Many service classes in this PR inherit fromServiceProtocolbut their constructors require additional parameters (for examplefluent_error_state,stub, etc.), which will make type checking fail because the subclass__init__is incompatible with the protocol. Consider either (a) removing__init__from the protocol (use it as a marker protocol), or (b) widening it to accept*args/**kwargs(or an optionalfluent_error_state) so implementations remain compatible.
src/ansys/fluent/core/services/solution_variables.py:229ZonesInfo.__getitem__changed fromdict.get(..., None)to direct indexing, which will now raiseKeyErrorfor unknown zone names. Because this class appears to be part of the user-facing API (and examples show indexing usage), this is a breaking behavior change. Consider preserving the previousNone-returning behavior for__getitem__, or documenting the change and encouraging callers to useget()when the key may be absent.
| T = TypeVar("T", bound=type) | ||
| E = TypeVar("E") | ||
|
|
||
|
|
||
| class _ConnectionInterface: | ||
| def __init__(self, create_grpc_service, error_state, supports_v1): | ||
| def __init__( | ||
| self, | ||
| create_grpc_service: Callable[[T, E], T], | ||
| error_state: E, | ||
| supports_v1: bool, | ||
| ): |
| reportUnusedCallResult = false | ||
| reportUnannotatedClassAttribute = false | ||
| ignore = ["doc", "src/ansys/fluent/core/generated"] | ||
| baselineFile = ".ci/basedpyright.json" |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 94 out of 95 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (1)
src/ansys/fluent/core/services/_protocols.py:38
ServiceProtocolcurrently constrains service constructors to__init__(channel, metadata)only. Many services (andFluentConnection.create_grpc_service, which forwards*args) require additional positional parameters (for examplefluent_error_state). Consider widening the protocol initializer to accept*args, **kwargs(or adding a second protocol for services that takefluent_error_state) so the protocol matches actual construction sites and doesn’t force type-checker ignores.
| from ansys.fluent.core.launcher.standalone_launcher import StandaloneLauncher | ||
| import ansys.fluent.core.launcher.watchdog as watchdog | ||
| from ansys.fluent.core.module_config import config | ||
| from ansys.fluent.core.session import BaseSession |
| rep = Report(ansys_libs=dependencies, ansys_vars=ANSYS_ENV_VARS) | ||
| assert "PyAnsys Software and Environment Report" in str(rep) | ||
| assert str(rep).count("pandas") == 1 | ||
| assert str(rep).count("pandas") == 2 | ||
|
|
Context
Duplicate of #4500 but on the ansys remote. Looking at this code more, I realise I've ran pyupgrade on this so don't merge this till I get the rest of the ruff stuff set up. I'm also realising there are 3 issues tied up in this, apologies for that.
Fixes #4489 and fixes #4490
Helps with #4543
Change Summary
Added a bunch of initial types for the public facing library part of #4738