Skip to content

feat: Initial bulk of type checking#4761

Open
Gobot1234 wants to merge 106 commits into
mainfrom
jhilton/typing-improvements
Open

feat: Initial bulk of type checking#4761
Gobot1234 wants to merge 106 commits into
mainfrom
jhilton/typing-improvements

Conversation

@Gobot1234
Copy link
Copy Markdown
Collaborator

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

Comment on lines +187 to +200
}
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(
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was changed as sadly there's no way to type transform a typedict to have optional keys (python isn't typescript, sad)

Comment thread src/ansys/fluent/core/launcher/launcher.py Outdated
Comment thread src/ansys/fluent/core/session_pure_meshing.py
Comment thread src/ansys/fluent/core/session_solver.py Outdated
@Gobot1234
Copy link
Copy Markdown
Collaborator Author

I'll try and get to the conflicts soonish, probably more useful to get the ruff changes in first

@github-actions github-actions Bot added documentation Documentation related (improving, adding, etc) examples Publishing PyFluent examples maintenance General maintenance of the repo (libraries, cicd, etc) dependencies Related to dependencies labels Jan 15, 2026
Comment thread src/ansys/fluent/core/launcher/slurm_launcher.py Outdated
@Gobot1234 Gobot1234 marked this pull request as ready for review January 15, 2026 03:19
Copilot AI review requested due to automatic review settings January 15, 2026 03:19
Gobot1234 and others added 2 commits April 30, 2026 10:34
Copilot AI review requested due to automatic review settings April 30, 2026 14:40
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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_launcher docstring says it raises ValueError for unknown launch modes, but the implementation raises DisallowedValuesError. Either update the docstring to match the actual exception type or change the raised exception to ValueError for 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 configuration dict when dry_run is 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.

Comment on lines +149 to +158
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)
Copy link

Copilot AI Apr 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Comment thread src/ansys/fluent/core/session_solver.py Outdated
Comment thread src/ansys/fluent/core/session_solver.py
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 *

Copy link

Copilot AI Apr 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
# 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

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 _argvals via _get_argvals_and_session(argvals) before applying config.show_fluent_gui / ui_mode. This can leave _argvals['graphics_driver'] inconsistent with the final ui_mode (because _get_graphics_driver() depends on ui_mode). Apply the show_fluent_gui logic before calling _get_argvals_and_session(), or recompute graphics_driver after updating ui_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.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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__, argvals is built from SlurmLauncherArgs.__annotations__, which likely omits fields inherited from LauncherArgsBase (e.g., start_timeout, cleanup_on_exit, env, etc.). Those keys are later accessed via self._argvals["start_timeout"], self._argvals["env"], etc., which can raise KeyError and/or silently drop user-provided values. Build argvals from kwargs directly (or merge in base TypedDict keys) so all launcher args are preserved, e.g., start from argvals = 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 *args into launch_fluent(), but launch_fluent() is now keyword-only (leading * in its signature). Any positional arguments passed to setup_for_fluent() will now raise TypeError. Consider updating setup_for_fluent to 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.

Comment on lines +224 to +229
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)
Copy link

Copilot AI May 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Comment on lines +354 to 364
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)):
Copy link

Copilot AI May 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 94 out of 95 changed files in this pull request and generated 2 comments.

Comment on lines +369 to +377
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,
Comment on lines +63 to 65
def callbacks(self) -> list[list[Callable | list | dict]]:
"""Get list of callbacks along with arguments and keyword arguments."""
return self._service_callbacks.values()
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

  • ServiceProtocol currently defines an __init__(channel, metadata) signature, but many concrete service classes that now inherit it have different constructor signatures (for example StreamingService.__init__(stub, metadata) and several services also take fluent_error_state). This will cause type checkers to report incompatible overrides and undermines the goal of improving typing. Consider making ServiceProtocol a 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 raises ValueError for an unknown launch mode, but the implementation raises DisallowedValuesError. 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 *), but setup_for_fluent() still accepts *args and forwards them directly. Any positional arguments passed to setup_for_fluent() will now raise TypeError at runtime. Consider making setup_for_fluent keyword-only as well (or validating that args is empty and raising a clearer error).

Comment on lines +185 to +187
launcher = StandaloneLauncher(
**kwargs, dry_run=dry_run, mode=cls._session_mode[cls.__name__]
)
Comment on lines +292 to +294
launcher = DockerLauncher(
**kwargs, dry_run=dry_run, mode=cls._session_mode[cls.__name__]
)
Comment on lines +198 to +210
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`.
"""
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

  • ServiceProtocol defines an __init__(channel, metadata) signature (under TYPE_CHECKING). Many service classes in this PR inherit from ServiceProtocol but their constructors require additional parameters (for example fluent_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 optional fluent_error_state) so implementations remain compatible.
    src/ansys/fluent/core/services/solution_variables.py:229
  • ZonesInfo.__getitem__ changed from dict.get(..., None) to direct indexing, which will now raise KeyError for 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 previous None-returning behavior for __getitem__, or documenting the change and encouraging callers to use get() when the key may be absent.

Comment on lines +369 to +379
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,
):
Comment thread pyproject.toml
reportUnusedCallResult = false
reportUnannotatedClassAttribute = false
ignore = ["doc", "src/ansys/fluent/core/generated"]
baselineFile = ".ci/basedpyright.json"
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

  • ServiceProtocol currently constrains service constructors to __init__(channel, metadata) only. Many services (and FluentConnection.create_grpc_service, which forwards *args) require additional positional parameters (for example fluent_error_state). Consider widening the protocol initializer to accept *args, **kwargs (or adding a second protocol for services that take fluent_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
Comment thread tests/test_launcher.py
Comment on lines 631 to 634
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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CI/CD dependencies Related to dependencies documentation Documentation related (improving, adding, etc) examples Publishing PyFluent examples maintenance General maintenance of the repo (libraries, cicd, etc) new feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Avoid having default None where a more informative default can be provided Make launch/connect_to_fluent and kwarg only

8 participants