Skip to content

Commit f2e5c17

Browse files
authored
Disallow keyword arguments on handlers with deserialized arguments (#265)
Fixes #226
1 parent df0a34f commit f2e5c17

File tree

6 files changed

+86
-2
lines changed

6 files changed

+86
-2
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -490,7 +490,8 @@ definition/behavior of the method:
490490
side effects, meaning they should never mutate state or try to wait on anything.
491491

492492
Workflows can be inherited, but subclass workflow-level decorators override superclass ones, and the same method can't
493-
be decorated with different handler types/names in the hierarchy.
493+
be decorated with different handler types/names in the hierarchy. Workflow handlers (execute or any marked method)
494+
cannot accept keyword arguments.
494495

495496
#### Running Workflows
496497

@@ -955,6 +956,7 @@ Some notes about activity definition:
955956
* `workflow_raw_args` can be used to have activity arguments delivered to `execute` as
956957
`Temporalio::Converters::RawValue`s. These are wrappers for the raw payloads that have not been converted to types
957958
(but they have been decoded by the codec if present). They can be converted with `payload_converter` on the context.
959+
* Activities cannot accept keyword arguments.
958960

959961
#### Activity Context
960962

temporalio/.rubocop.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ Metrics/ModuleLength:
6767

6868
# The default is too small
6969
Metrics/PerceivedComplexity:
70-
Max: 40
70+
Max: 60
7171

7272
# We want the `*args` syntax instead of `*` so we can document clearly in YARD
7373
Style/ArgumentsForwarding:

temporalio/lib/temporalio/activity/definition.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,11 @@ def self._activity_definition_details
8585
activity_name = @activity_name
8686
raise 'Cannot have activity name specified for dynamic activity' if activity_name && @activity_dynamic
8787

88+
# Disallow kwargs in execute parameters
89+
if instance_method(:execute).parameters.any? { |t, _| t == :key || t == :keyreq }
90+
raise 'Activity execute cannot have keyword arguments'
91+
end
92+
8893
# Default to unqualified class name if not dynamic
8994
activity_name ||= name.to_s.split('::').last unless @activity_dynamic
9095
{

temporalio/lib/temporalio/workflow/definition.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,15 @@ def self.method_added(method_name)
235235
# Reset details
236236
self.pending_handler_details = nil
237237

238+
# Disallow kwargs in parameters
239+
begin
240+
if instance_method(method_name).parameters.any? { |t, _| t == :key || t == :keyreq }
241+
raise "Workflow #{handler[:type]} cannot have keyword arguments"
242+
end
243+
rescue NameError
244+
# Ignore name error
245+
end
246+
238247
# Initialize class variables if not done already
239248
@workflow_signals ||= {}
240249
@workflow_queries ||= {}
@@ -350,6 +359,11 @@ def self._build_workflow_definition
350359
raise "Leftover #{pending_handler_details&.[](:type)} handler not applied to a method"
351360
end
352361

362+
# Disallow kwargs in execute parameters
363+
if instance_method(:execute).parameters.any? { |t, _| t == :key || t == :keyreq }
364+
raise 'Workflow execute cannot have keyword arguments'
365+
end
366+
353367
# Apply all update validators before merging with super
354368
updates = @workflow_updates&.dup || {}
355369
@workflow_update_validators&.each_value do |validator|

temporalio/test/worker_activity_test.rb

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -924,6 +924,17 @@ def test_reserved_name
924924
assert_includes err.message, "'__temporal_activity' cannot start with '__temporal_'"
925925
end
926926

927+
class KeywordArgumentActivity < Temporalio::Activity::Definition
928+
def execute(foo, bar: 'baz'); end
929+
end
930+
931+
def test_keyword_arguments
932+
err = assert_raises { Temporalio::Activity::Definition::Info.from_activity(KeywordArgumentActivity) }
933+
assert_includes err.message, 'Activity execute cannot have keyword arguments'
934+
err = assert_raises { Temporalio::Activity::Definition::Info.from_activity(KeywordArgumentActivity.new) }
935+
assert_includes err.message, 'Activity execute cannot have keyword arguments'
936+
end
937+
927938
# steep:ignore
928939
def execute_activity(
929940
activity,

temporalio/test/workflow/definition_test.rb

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,5 +343,57 @@ def some_update; end
343343
end
344344
CODE
345345
end
346+
347+
def test_kwargs
348+
assert_invalid_workflow_code 'Workflow execute cannot have keyword arguments', <<~CODE
349+
class TestExecuteKeywordArgs < Temporalio::Workflow::Definition
350+
def execute(foo, bar:)
351+
end
352+
end
353+
CODE
354+
assert_invalid_workflow_code 'Workflow init cannot have keyword arguments', <<~CODE
355+
class TestInitKeywordArgs < Temporalio::Workflow::Definition
356+
workflow_init
357+
def initialize(foo, bar:); end
358+
359+
def execute; end
360+
end
361+
CODE
362+
assert_invalid_workflow_code 'Workflow signal cannot have keyword arguments', <<~CODE
363+
class TestSignalKeywordArgs < Temporalio::Workflow::Definition
364+
def execute; end
365+
366+
workflow_signal
367+
def some_handler(foo, bar: 'baz'); end
368+
end
369+
CODE
370+
assert_invalid_workflow_code 'Workflow query cannot have keyword arguments', <<~CODE
371+
class TestQueryKeywordArgs < Temporalio::Workflow::Definition
372+
def execute; end
373+
374+
workflow_query
375+
def some_handler(foo, bar:); end
376+
end
377+
CODE
378+
assert_invalid_workflow_code 'Workflow update cannot have keyword arguments', <<~CODE
379+
class TestUpdateKeywordArgs < Temporalio::Workflow::Definition
380+
def execute; end
381+
382+
workflow_update
383+
def some_handler(foo, bar:); end
384+
end
385+
CODE
386+
assert_invalid_workflow_code 'Workflow update_validator cannot have keyword arguments', <<~CODE
387+
class TestUpdateValidatorKeywordArgs < Temporalio::Workflow::Definition
388+
def execute; end
389+
390+
workflow_update_validator(:some_handler)
391+
def validate_some_handler(foo, bar:); end
392+
393+
workflow_update
394+
def some_handler(foo, bar:); end
395+
end
396+
CODE
397+
end
346398
end
347399
end

0 commit comments

Comments
 (0)