99from typing import Any , Final
1010
1111from mypy import defaults
12- from mypy .errorcodes import ErrorCode , error_codes
12+ from mypy .errorcodes import ErrorCode , error_codes , error_codes_on_by_default
1313from mypy .util import get_class_descriptors , replace_object_state
1414
1515
@@ -29,7 +29,6 @@ class BuildType:
2929 "check_untyped_defs" ,
3030 "debug_cache" ,
3131 "disable_error_code" ,
32- "disabled_error_codes" ,
3332 "disallow_any_decorated" ,
3433 "disallow_any_explicit" ,
3534 "disallow_any_expr" ,
@@ -41,7 +40,6 @@ class BuildType:
4140 "disallow_untyped_decorators" ,
4241 "disallow_untyped_defs" ,
4342 "enable_error_code" ,
44- "enabled_error_codes" ,
4543 "extra_checks" ,
4644 "follow_imports_for_stubs" ,
4745 "follow_imports" ,
@@ -254,13 +252,15 @@ def __init__(self) -> None:
254252 # Variable names considered False
255253 self .always_false : list [str ] = []
256254
257- # Error codes to disable
255+ # The set of error codes (as ErrorCode objects) we've computed as
256+ # being actually enabled or disabled after all is said and done.
257+ self .active_error_codes : set [ErrorCode ] = error_codes_on_by_default .copy ()
258+
259+ # List of error codes we are being instructed to disable
258260 self .disable_error_code : list [str ] = []
259- self .disabled_error_codes : set [ErrorCode ] = set ()
260261
261- # Error codes to enable
262+ # List of error codes we are being instructed to enable
262263 self .enable_error_code : list [str ] = []
263- self .enabled_error_codes : set [ErrorCode ] = set ()
264264
265265 # Use script name instead of __main__
266266 self .scripts_are_modules = False
@@ -437,7 +437,11 @@ def __repr__(self) -> str:
437437 return f"Options({ pprint .pformat (self .snapshot ())} )"
438438
439439 def process_error_codes (self , * , error_callback : Callable [[str ], Any ]) -> None :
440- """Process `--enable-error-code` and `--disable-error-code` flags."""
440+ # This function seems to be standalone so that it can be called after plugins, in various places.
441+ """Process `enable-error-code` and `disable-error-code` flags.
442+ This clears those fields, which were temporary, and only used for option injestion.
443+ The persistant field to consult about error code enablement is active_error_code.
444+ """
441445 disabled_code_names = set (self .disable_error_code )
442446 enabled_code_names = set (self .enable_error_code )
443447
@@ -448,12 +452,11 @@ def process_error_codes(self, *, error_callback: Callable[[str], Any]) -> None:
448452 ) - valid_error_code_names
449453 if invalid_code_names_here :
450454 error_callback (f"Invalid error code(s): { ', ' .join (sorted (invalid_code_names_here ))} " )
451-
452- self .disabled_error_codes |= {error_codes [code ] for code in disabled_code_names }
453- self .enabled_error_codes |= {error_codes [code ] for code in enabled_code_names }
454-
455- # Enabling an error code always overrides disabling
456- self .disabled_error_codes -= self .enabled_error_codes
455+ #TODO(Wyatt): I don't think the new way accounts for subcodes
456+ self .active_error_codes -= {error_codes [code ] for code in disabled_code_names }
457+ self .active_error_codes |= {error_codes [code ] for code in enabled_code_names }
458+ self .disable_error_code .clear ()
459+ self .enable_error_code .clear ()
457460
458461 def process_incomplete_features (
459462 self , * , error_callback : Callable [[str ], Any ], warning_callback : Callable [[str ], Any ]
@@ -475,41 +478,32 @@ def process_strict_bytes(self) -> None:
475478 # forwards compatibility
476479 self .strict_bytes = True
477480
478- def apply_changes (self , changes : dict [str , object ]) -> Options :
479- # Note: effects of this method *must* be idempotent.
481+ def copy_with_changes (self , changes : dict [str , object ]) -> Options :
482+ """Return a new Options object that has the changes applied over the old one.
483+ Note: effects of this method *must* be idempotent."""
480484 new_options = Options ()
481485 # Under mypyc, we don't have a __dict__, so we need to do worse things.
482486 replace_object_state (new_options , self , copy_dict = True )
483487 for key , value in changes .items ():
484488 setattr (new_options , key , value )
485489 if changes .get ("ignore_missing_imports" ):
486490 # This is the only option for which a per-module and a global
487- # option sometimes beheave differently.
491+ # option sometimes behave differently.
488492 new_options .ignore_missing_imports_per_module = True
489-
490- # These two act as overrides, so apply them when cloning.
491- # Similar to global codes enabling overrides disabling, so we start from latter.
492- new_options .disabled_error_codes = self .disabled_error_codes .copy ()
493- new_options .enabled_error_codes = self .enabled_error_codes .copy ()
494- for code_str in new_options .disable_error_code :
495- code = error_codes [code_str ]
496- new_options .disabled_error_codes .add (code )
497- new_options .enabled_error_codes .discard (code )
498- for code_str in new_options .enable_error_code :
499- code = error_codes [code_str ]
500- new_options .enabled_error_codes .add (code )
501- new_options .disabled_error_codes .discard (code )
493+ new_options .process_error_codes (
494+ error_callback = lambda x : print (x , sys .stderr and exit (x )) if x else None
495+ )
502496 return new_options
503497
504498 def compare_stable (self , other_snapshot : dict [str , object ]) -> bool :
505- """Compare options in a way that is stable for snapshot() -> apply_changes () roundtrip.
499+ """Compare options in a way that is stable for snapshot() -> copy_with_changes () roundtrip.
506500
507- This is needed because apply_changes () has non-trivial effects for some flags, so
508- Options().apply_changes (options.snapshot()) may result in a (slightly) different object.
501+ This is needed because copy_with_changes () has non-trivial effects for some flags, so
502+ Options().copy_with_changes (options.snapshot()) may result in a (slightly) different object.
509503 """
510504 return (
511- Options ().apply_changes (self .snapshot ()).snapshot ()
512- == Options ().apply_changes (other_snapshot ).snapshot ()
505+ Options ().copy_with_changes (self .snapshot ()).snapshot ()
506+ == Options ().copy_with_changes (other_snapshot ).snapshot ()
513507 )
514508
515509 def build_per_module_cache (self ) -> None :
@@ -549,7 +543,7 @@ def build_per_module_cache(self) -> None:
549543 # on inheriting from parent configs.
550544 options = self .clone_for_module (key )
551545 # And then update it with its per-module options.
552- self ._per_module_cache [key ] = options .apply_changes (self .per_module_options [key ])
546+ self ._per_module_cache [key ] = options .copy_with_changes (self .per_module_options [key ])
553547
554548 # Add the more structured sections into unused configs, since
555549 # they only count as used if actually used by a real module.
@@ -590,7 +584,7 @@ def clone_for_module(self, module: str) -> Options:
590584 for key , pattern in self ._glob_options :
591585 if pattern .match (module ):
592586 self .unused_configs .discard (key )
593- options = options .apply_changes (self .per_module_options [key ])
587+ options = options .copy_with_changes (self .per_module_options [key ])
594588
595589 # We could update the cache to directly point to modules once
596590 # they have been looked up, but in testing this made things
@@ -612,7 +606,7 @@ def select_options_affecting_cache(self) -> dict[str, object]:
612606 result : dict [str , object ] = {}
613607 for opt in OPTIONS_AFFECTING_CACHE :
614608 val = getattr (self , opt )
615- if opt in ( "disabled_error_codes" , "enabled_error_codes" ) :
609+ if opt == "active_error_codes" :
616610 val = sorted ([code .code for code in val ])
617611 result [opt ] = val
618612 return result
0 commit comments