Skip to content

Latest commit

 

History

History
131 lines (97 loc) · 4.56 KB

tconfiguration.md

File metadata and controls

131 lines (97 loc) · 4.56 KB
id title
tconfiguration
Runtime Configuration

Recall from Enabling runtime checks that sorbet-runtime raises exceptions for type errors that happen at runtime. This is to ensure that even code that is not covered by types statically can still benefit from type information, and that authors don't have to defend against their typed methods being called incorrectly.

The behavior of the sorbet-runtime package can be configured by registering callbacks before a program is run. While it's possible to use these callbacks to silence all runtime exceptions raised by sorbet-runtime, we strongly recommend not doing so. Runtime exceptions have proven to be invaluable in gaining confidence in the correctness of type signatures as we rolled out types to Stripe's Ruby codebase.

Note: Think carefully before disabling runtime checks!

These docs are somewhat low-level. For a higher-level description of how to change Sorbet's runtime, see Enabling Runtime Checks.

Errors from inline type assertions

There are four kinds of inline type assertions:

  • T.let(expr, Type)
  • T.cast(expr, Type)
  • T.must(expr)
  • T.assert_type!(expr, Type)

To customize the behavior when one of these assertions fails:

T::Configuration.inline_type_error_handler = lambda do |error, opts|
  puts error.message
end

The default error handler is to raise (a TypeError).

Note that setting this callback will not handle type errors raised when a method with a signature is called incorrectly. For those, see the next section.

Errors from invalid method calls

To customize the behavior when a method with a sig is called and the argument types or return types don't match the actual value present at runtime, use this:

T::Configuration.call_validation_error_handler = lambda do |signature, opts|
  puts opts[:message]
end

The default error handler is to raise an error.

The example handler above does the same thing for every method. One thing which can be useful is to pass custom metadata to the call_validation handler, which can be done with .on_failure:

T::Configuration.call_validation_error_handler = lambda do |signature, opts|
  if signature.on_failure
    puts "Metadata: #{signature.on_failure}"
  end
  raise TypeError.new(opts[:pretty_message])
end

sig {params(x: Integer).void.on_failure(:hello, :world)}
def foo(x); end

When the call_validation_error_handler is called this time, it will be passed all the args that were given to on_failure for the specific sig that failed.

Errors from invalid sig procs

We write sigs using valid Ruby syntax. The body of the proc passed to a sig is executed (lazily, on first method call) to compute an in-memory data structure representing that sig's types. The execution of this proc can be invalid (for example, if returns or void is never called).

The default behavior when building a sig is invalid is to raise an ArgumentError. To customize this behavior, use this:

T::Configuration.sig_builder_error_handler = lambda do |error, location|
  puts error.message
end

Errors from invalid sigs

Method signatures that build correctly can still be invalid. For example, a sig marked override must actually override a method. Same for abstract methods. When overriding a parent sig, the variance must match on the input and output types. If a sig that built correctly is invalid in anyway, this error handler will be called:

T::Configuration.sig_validation_error_handler = lambda do |error, opts|
  puts error.message
end

Environment variables

There are a number of environment variables that sorbet-runtime reads from to change its behavior:

SORBET_RUNTIME_ENABLE_CHECKING_IN_TESTS

Announces to Sorbet that we are currently in a test environment, so it should treat any sigs which are marked .checked(:tests) as if they were just a normal sig. This can be set to any truthy value to take effect.

This can also be done by calling T::Configuration.enable_checking_for_sigs_marked_checked_tests but the environment variable ensures this value gets set before any sigs are evaluated.

SORBET_RUNTIME_DEFAULT_CHECKED_LEVEL

Configure the default checked level for a sig with no explicit .checked builder. When unset, the default checked level is :always. This must be set to a valid checked level (for example: always, tests, or never).

This can also be done by calling T::Configuration.default_checked_level = ... but the environment variable ensures this value gets set before any sigs are evaluated.