Scenarios of behaviour to check

This library is built on the idea of splitting all of our checks into multiple isolated scenarios where each scenario can focus on enforcing some specific requirement. These are then strung together using the test runner.

These scenarios are either a check on the view themself, or a check on the pattern and must subclass one of these two base classes:

class django_consistency_enforcer.urls.PatternScenario

Represents a check that is run on a specific pattern.

abstract property exit_early : bool

Used to indicate that if this scenario fails, no other pattern scenarios should be run.

abstractmethod run(*, errors: ErrorContainer, auth_user_model: type, pattern: T_Pattern) None

This is where the logic for the check needs to go.

class django_consistency_enforcer.urls.FunctionScenario

Represents a check that is run on a specific Django view.

abstract property exit_early : bool

Used to indicate that if this scenario fails, no other pattern scenarios should be run.

abstractmethod run(*, errors: ErrorContainer, auth_user_model: type, pattern: T_Pattern, function: DispatchFunction) None

This is where the logic for the check needs to go.

Built in scenarios

class django_consistency_enforcer.urls.CheckAcceptsArgsFunctionScenario(exit_early: bool = attr_dict['exit_early'].default)

Checks that all the arguments in the pattern are accepted by the function.

add_error(*, errors: ErrorContainer, error: ViewDoesNotAcceptCapturedArg) None

Add an error to come out of the scenario.

exit_early : bool
run(*, errors: ErrorContainer, auth_user_model: type, pattern: T_ViewPattern, function: DispatchFunction) None

If the function has a **kwargs: object or **kwargs: Any then no errors because the function takes in anything.

Otherwise we gather the keyword arguments on the function and match them to the captured args found on the pattern.

class django_consistency_enforcer.urls.CheckHasCorrectAnnotationsFunctionScenario(exit_early: bool = attr_dict['exit_early'].default)

Check that the annotations on the function args match the types provided by the converters used in the captured groups of the pattern.

add_error(*, errors: ErrorContainer, error: InvalidArgAnnotations) None

Add an error to come out of the scenario.

exit_early : bool
run(*, errors: ErrorContainer, auth_user_model: type, pattern: T_ViewPattern, function: DispatchFunction) None

This matches the annotations on the function args to the annotations on the captured args on the pattern.

class django_consistency_enforcer.urls.CheckKwargsMustBeAnnotatedFunctionScenario(allows_object: bool = attr_dict['allows_object'].default, allows_any: bool = attr_dict['allows_any'].default, exit_early: bool = attr_dict['exit_early'].default)

Check that a **kwargs on the django view is annotated correctly.

add_error(*, errors: ErrorContainer, error: KwargsMustBeAnnotated) None

Add an error to come out of the scenario.

allows_any : bool
allows_object : bool
exit_early : bool
run(*, errors: ErrorContainer, auth_user_model: type, pattern: T_Pattern, function: DispatchFunction) None

Ensure that the annotation is correct

  • Annotation of object is correct only if self.allows_object

  • Annotation of Any is correct only if self.allows_any

  • Otherwise complain because the only valid annotation otherwise is a TypedDict and those are already unpacked when we create the function objects.

class django_consistency_enforcer.urls.CheckPositionalArgsAreCorrectFunctionScenario(disallow_var_args: bool = attr_dict['disallow_var_args'].default, enforce_keyword_args: bool = attr_dict['enforce_keyword_args'].default, exit_early: bool = attr_dict['exit_early'].default)

This is a scenario that checks that the positional arguments on a view are correct.

This view needs to be subclass’d with an implementation for is_mistyped.

add_error(*, errors: ErrorContainer, error: MismatchedRequiredArgs) None

Add an error to come out of the scenario.

disallow_var_args : bool
enforce_keyword_args : bool
exit_early : bool
abstractmethod is_mistyped(*, function: DispatchFunction, want_annotation: object, got_annotation: object, auth_user_model: type, name: str, position: int) Incorrect | None

Used to determine what error is appropriate when the annotation doesn’t match for any of the required positional arguments.

This is abstract cause deciding that is likely project specific.

run(*, errors: ErrorContainer, auth_user_model: type, pattern: T_Pattern, function: DispatchFunction) None

This check looks at all the function_args and:

  • Skips the self argument

  • Matches the positional arguments found on the view against the required arguments defined by function

    • Complains about *args if self.disallow_var_args

    • Complains about a missing * after required positional args if self.enforce_keyword_args

    • Calls into is_mistyped to get an error if the annotation on the argument is neither Any or the expected annotation for that argument.

class django_consistency_enforcer.urls.CheckRequiredArgsMatchUrlPatternFunctionScenario(exit_early: bool = attr_dict['exit_early'].default)

Checks that the arguments that are required by a function are found in the pattern that routes to the view.

add_error(*, errors: ErrorContainer, error: RequiredArgOnViewNotAlwaysRequiredByPattern) None

Add an error to come out of the scenario.

exit_early : bool
run(*, errors: ErrorContainer, auth_user_model: type, pattern: T_ViewPattern, function: DispatchFunction) None

Discover the arguments that must be passed into the function and complain about any that are missing from the pattern.

class django_consistency_enforcer.urls.CheckViewClassRequestAnnotationScenario(acceptable_annotations: Sequence[object], acceptable_request_annotation_containers: Sequence[object], error_class: type[InvalidRequestAnnotation], exit_early: bool = attr_dict['exit_early'].default)

Check that the view class has an annotation for request that is correct.

acceptable_annotations : Sequence[object]
acceptable_request_annotation_containers : Sequence[object]
add_error(*, errors: ErrorContainer, error: InvalidRequestAnnotation) None

Add an error to come out of the scenario.

annotation_is_valid(*, errors: ErrorContainer, auth_user_model: type, annotation: object, pattern: T_ViewPattern) bool

Return whether the annotation found for request is valid.

This can be subclass’d when a project has it’s own specific rules.

error_class : type[InvalidRequestAnnotation]
exit_early : bool
run(*, errors: ErrorContainer, auth_user_model: type, pattern: T_ViewPattern) None

If the pattern is not a view class method or there is no request annotation on the class then there is nothing to check and we exit.

Otherwise we defer to annotation_is_valid to determine if the annotation is correct.