Skip to content

Commit 4b76c44

Browse files
chore: prevent invalid kwargs from being passed to launcher methods (#2293)
Co-authored-by: pyansys-ci-bot <[email protected]>
1 parent 58cb516 commit 4b76c44

File tree

6 files changed

+120
-29
lines changed

6 files changed

+120
-29
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Prevent invalid kwargs from being passed to launcher methods

src/ansys/geometry/core/connection/launcher.py

Lines changed: 14 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,11 @@
3535
)
3636
from ansys.geometry.core.connection.product_instance import prepare_and_start_backend
3737
from ansys.geometry.core.logger import LOG
38-
from ansys.geometry.core.misc.checks import check_type, deprecated_argument
38+
from ansys.geometry.core.misc.checks import (
39+
check_type,
40+
deprecated_argument,
41+
kwargs_passed_not_accepted,
42+
)
3943

4044
try:
4145
import ansys.platform.instancemanagement as pypim
@@ -233,6 +237,7 @@ def _launch_with_automatic_detection(**kwargs: dict | None) -> "Modeler":
233237
raise NotImplementedError("Geometry service cannot be initialized.")
234238

235239

240+
@kwargs_passed_not_accepted
236241
def launch_remote_modeler(
237242
platform: str = "windows",
238243
version: str | None = None,
@@ -287,6 +292,7 @@ def launch_remote_modeler(
287292
)
288293

289294

295+
@kwargs_passed_not_accepted
290296
def launch_docker_modeler(
291297
port: int = pygeom_defaults.DEFAULT_PORT,
292298
connect_to_existing_service: bool = True,
@@ -362,6 +368,7 @@ def launch_docker_modeler(
362368
)
363369

364370

371+
@kwargs_passed_not_accepted
365372
def launch_modeler_with_discovery_and_pimlight(
366373
version: str | None = None,
367374
client_log_level: int = logging.INFO,
@@ -404,6 +411,7 @@ def launch_modeler_with_discovery_and_pimlight(
404411
)
405412

406413

414+
@kwargs_passed_not_accepted
407415
def launch_modeler_with_geometry_service_and_pimlight(
408416
version: str | None = None,
409417
client_log_level: int = logging.INFO,
@@ -446,6 +454,7 @@ def launch_modeler_with_geometry_service_and_pimlight(
446454
)
447455

448456

457+
@kwargs_passed_not_accepted
449458
def launch_modeler_with_spaceclaim_and_pimlight(
450459
version: str | None = None,
451460
client_log_level: int = logging.INFO,
@@ -488,6 +497,7 @@ def launch_modeler_with_spaceclaim_and_pimlight(
488497
)
489498

490499

500+
@kwargs_passed_not_accepted
491501
@deprecated_argument("product_version", "version", version="0.10.8", remove="0.13.0")
492502
def launch_modeler_with_geometry_service(
493503
version: str | int | None = None,
@@ -587,13 +597,6 @@ def launch_modeler_with_geometry_service(
587597
timeout=300,
588598
server_log_level=0)
589599
"""
590-
# if api_version is passed, throw a warning saying that it is not used
591-
if "api_version" in kwargs:
592-
LOG.warning(
593-
"The 'api_version' parameter is not used in 'launch_modeler_with_geometry_service'. "
594-
"Please remove it from the arguments."
595-
)
596-
597600
# If we are in a Windows environment, we are going to write down the server
598601
# logs in the %PUBLIC%/Documents/Ansys/GeometryService folder.
599602
if os.name == "nt" and server_logs_folder is None:
@@ -617,6 +620,7 @@ def launch_modeler_with_geometry_service(
617620
)
618621

619622

623+
@kwargs_passed_not_accepted
620624
@deprecated_argument("product_version", "version", version="0.10.8", remove="0.13.0")
621625
def launch_modeler_with_discovery(
622626
version: str | int | None = None,
@@ -720,13 +724,6 @@ def launch_modeler_with_discovery(
720724
timeout=300,
721725
server_log_level=0)
722726
"""
723-
for unused_var in ["server_logs_folder", "logs_folder"]:
724-
if unused_var in kwargs:
725-
LOG.warning(
726-
f"The '{unused_var}' parameter is not used in 'launch_modeler_with_discovery'. "
727-
"Please remove it from the arguments."
728-
)
729-
730727
return prepare_and_start_backend(
731728
BackendType.DISCOVERY,
732729
version=version,
@@ -744,6 +741,7 @@ def launch_modeler_with_discovery(
744741
)
745742

746743

744+
@kwargs_passed_not_accepted
747745
@deprecated_argument("product_version", "version", version="0.10.8", remove="0.13.0")
748746
def launch_modeler_with_spaceclaim(
749747
version: str | int | None = None,
@@ -847,13 +845,6 @@ def launch_modeler_with_spaceclaim(
847845
timeout=300,
848846
server_log_level=0)
849847
"""
850-
for unused_var in ["server_logs_folder", "logs_folder"]:
851-
if unused_var in kwargs:
852-
LOG.warning(
853-
f"The '{unused_var}' parameter is not used in 'launch_modeler_with_spaceclaim'. "
854-
"Please remove it from the arguments."
855-
)
856-
857848
return prepare_and_start_backend(
858849
BackendType.SPACECLAIM,
859850
version=version,
@@ -871,6 +862,7 @@ def launch_modeler_with_spaceclaim(
871862
)
872863

873864

865+
@kwargs_passed_not_accepted
874866
@deprecated_argument("product_version", "version", version="0.10.8", remove="0.13.0")
875867
def launch_modeler_with_core_service(
876868
version: str | int | None = None,
@@ -970,13 +962,6 @@ def launch_modeler_with_core_service(
970962
timeout=300,
971963
server_log_level=0)
972964
"""
973-
# if api_version is passed, throw a warning saying that it is not used
974-
if "api_version" in kwargs:
975-
LOG.warning(
976-
"The 'api_version' parameter is not used in 'launch_modeler_with_core_service'. "
977-
"Please remove it from the arguments."
978-
)
979-
980965
# If we are in a Windows environment, we are going to write down the server
981966
# logs in the %PUBLIC%/Documents/Ansys/GeometryService folder.
982967
if os.name == "nt" and server_logs_folder is None:

src/ansys/geometry/core/misc/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
deprecated_argument,
4646
deprecated_method,
4747
graphics_required,
48+
kwargs_passed_not_accepted,
4849
min_backend_version,
4950
run_if_graphics_required,
5051
)

src/ansys/geometry/core/misc/checks.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -478,3 +478,79 @@ def wrapper(*args, **kwargs):
478478
return method(*args, **kwargs)
479479

480480
return wrapper
481+
482+
483+
def kwargs_passed_not_accepted(method):
484+
"""Check that no unexpected kwargs are passed to the method.
485+
486+
This decorator will raise a TypeError if any keyword arguments are passed
487+
to the decorated method that don't correspond to the method's parameters.
488+
If the method has ``**kwargs`` in its signature, this decorator will raise an
489+
error for any kwargs passed to it (that are not explicitly accepted).
490+
491+
Parameters
492+
----------
493+
method : callable
494+
The method to decorate.
495+
496+
Returns
497+
-------
498+
callable
499+
Decorated method that raises TypeError if unexpected kwargs are passed.
500+
501+
Raises
502+
------
503+
TypeError
504+
If unexpected keyword arguments are passed to the decorated method.
505+
506+
Examples
507+
--------
508+
>>> @kwargs_passed_not_accepted
509+
... def my_method(arg1, arg2):
510+
... return arg1 + arg2
511+
>>> my_method(1, 2) # Works fine
512+
3
513+
>>> my_method(arg1=1, arg2=2) # Works fine
514+
3
515+
>>> my_method(1, 2, invalid_arg=3) # Raises TypeError
516+
TypeError: The following keyword arguments are not accepted
517+
in the method 'my_method': invalid_arg.
518+
>>> @kwargs_passed_not_accepted
519+
... def my_method_with_kwargs(arg1, arg2, **kwargs):
520+
... return arg1 + arg2
521+
>>> my_method_with_kwargs(1, 2, invalid_arg=3) # Raises TypeError
522+
TypeError: The following keyword arguments are not accepted
523+
in the method 'my_method_with_kwargs': invalid_arg.
524+
"""
525+
import inspect
526+
527+
def wrapper(*args, **kwargs):
528+
# Get the method signature
529+
sig = inspect.signature(method)
530+
531+
# Check if method has **kwargs parameter
532+
has_var_keyword = any(
533+
param.kind == inspect.Parameter.VAR_KEYWORD for param in sig.parameters.values()
534+
)
535+
536+
# If method has **kwargs and kwargs are passed...
537+
if has_var_keyword and len(kwargs) > 0:
538+
# Retrieve a list of all parameter names excluding **kwargs
539+
param_names = {
540+
name
541+
for name, param in sig.parameters.items()
542+
if param.kind != inspect.Parameter.VAR_KEYWORD
543+
}
544+
545+
# Identify unexpected kwargs
546+
unexpected_kwargs = [key for key in kwargs.keys() if key not in param_names]
547+
548+
if unexpected_kwargs:
549+
raise TypeError(
550+
"The following keyword arguments are not accepted in the"
551+
+ f" method '{method.__name__}': {', '.join(unexpected_kwargs)}."
552+
)
553+
554+
return method(*args, **kwargs)
555+
556+
return wrapper
File renamed without changes.

tests/test_misc_checks.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
check_type_equivalence,
4141
deprecated_argument,
4242
deprecated_method,
43+
kwargs_passed_not_accepted,
4344
min_backend_version,
4445
)
4546

@@ -411,3 +412,30 @@ def deprecated_argument_with_info_and_alternate_and_versions(
411412
with warnings.catch_warnings():
412413
warnings.simplefilter("error")
413414
mock_object.deprecated_argument_with_info_and_alternate_and_versions(alt_arg="test")
415+
416+
417+
def test_kwargs_passed_not_accepted():
418+
"""Test the kwargs_passed_not_accepted decorator."""
419+
420+
@kwargs_passed_not_accepted
421+
def my_method(arg1, arg2, **kwargs):
422+
"""A method that accepts no keyword arguments."""
423+
return arg1 + arg2
424+
425+
# Call the method without kwargs - should not raise an error
426+
assert my_method(1, 2) == 3
427+
assert my_method(arg1=1, arg2=2) == 3
428+
429+
# Call the method with kwargs - should raise an error
430+
with pytest.raises(
431+
TypeError,
432+
match="The following keyword arguments are not accepted in the"
433+
" method 'my_method': unexpected_arg, another_one.",
434+
):
435+
my_method(1, 2, unexpected_arg=3, another_one="test")
436+
437+
with pytest.raises(
438+
TypeError,
439+
match="The following keyword arguments are not accepted in the method 'my_method': arg3.",
440+
):
441+
my_method(arg1=1, arg2=2, arg3=3)

0 commit comments

Comments
 (0)