Representing views and patterns

A big part of this library is extracting information about the Django project for the checks that are performed to make sure that details about how to get this information does not affect what checks are made.

Every time a request comes into a Django server, that request is routed to a view via the urlpatterns defined by the project, such that the view gets given that request object and any information that is extracted from the request by that urlpattern.

So we need objects that represent: * What patterns exist * What view they call out to * What information gets extracted from the url pattern * What information the view accepts and requires

These objects can be split into two groups, objects representing the views, and objects representing the patterns.

Django views

class django_consistency_enforcer.urls.FunctionArg(name: str, required: bool, keyword_only: bool, annotation: object, is_self: bool = attr_dict['is_self'].default, is_variable_keywords: bool = attr_dict['is_variable_keywords'].default, is_variable_positional: bool = attr_dict['is_variable_positional'].default)

Holds onto the information for a single argument on a django view function/method.

annotation : object

The annotation associated with this argument

is_self : bool

Whether this argument represents the instance passed into a method (idiomatically, the self argument)

is_variable_keywords : bool

Whether this argument is a **kwargs

is_variable_positional : bool

Whether this argument is a *args

keyword_only : bool

Whether this argument can only be passed in as a keyword

matches(annotation: object) bool

Returns whether the annotation on the argument matches some specific annotation.

Takes into account:

  • Matches if this argument is typed as Any

  • Matches if this argument is **kwargs: object

  • Union types on both this arg and the provided annotation

name : str

The name of the argument

required : bool

Whether python will fail if this argument is not passed into the function

class django_consistency_enforcer.urls.Function(name: str, module: str, function_args: Sequence[FunctionArg], allows_arbitrary: bool, view_class: type | None = attr_dict['view_class'].default, defined_on: type | None = attr_dict['defined_on'].default)

Holds onto the information to a specific django function/method.

allows_arbitrary : bool

Whether there is a **kwargs: object or **kwargs: Any

defined_on : type | None

The specific class this function comes from

(may be different to view_class if defined on a parent)

display(*, indent: str = ' ') str

Return a string representation of the information held by this object.

Will be a multi line string of one line per specific information, with the specified indent. Does not include function args.

classmethod from_callback(callback: Callable[[...], object], view_class: type | None = None) Self

Return an instance of this class provided some function and the view class it was found on if it’s a method.

Will attempt to resolve any stringified annotations and do things like work out which class in the MRO the function is defined on if it’s a method.

Will also find any **kwargs: Unpack[…] and treat those as if they were defined as individual keyword arguments.

function_args : Sequence[FunctionArg]

The arguments to this function

module : str

The import path to the module the function is defined in

name : str

The name of the function

view_class : type | None

The class this function is defined on if it’s a method

class django_consistency_enforcer.urls.DispatchFunction(function: Function, positional: Sequence[tuple[str, object]])

This represents a Function that is used as by django for dispatching a request. Essentially it proxies a Function and also includes an understanding of any required positional arguments.

property allows_arbitrary : bool

Proxy allows_arbitrary from the function

property defined_on : type | None

Proxy defined_on from the function

display(**kwargs: Unpack[_DisplayArgs]) str

Proxy display from the function

classmethod from_callback(callback: Callable[[...], object], view_class: type | None = None, *, positional: Sequence[tuple[str, object]]) Self

Create an instance given some callback, the view it is found on if it’s a method, and the positional arguments that are considered required.

property function_args : Sequence[FunctionArg]

Proxy function_args from the function

property module : str

Proxy module from the function

property name : str

Proxy name from the function

positional : Sequence[tuple[str, object]]

Any required arguments as a tuple of (name, annotation) that is expected of this function

property view_class : type | None

Proxy view_class from the function

class django_consistency_enforcer.urls.Where(name: str, regex: str, module: str, namespace: str)

Used to say where an error with a url pattern can be found

display(*, indent: str = ' ', display_regex: bool = True) str

Return a string representing this location.

Essentially a string with each property on it’s own line indented the amount as specified.

classmethod empty() Self

Used to construct an instance where all the values are empty.

Useful in tests especially to create an instance of this class where we don’t need to worry about the details it’s actually representing.

classmethod from_resolver(resolver: URLResolver, *, regex: str, name: str | None = None, namespace: str | None = None) Self

Used to construct an instance taking into account the type of the urlconf_module on a url resolver.

module : str
name : str
namespace : str
regex : str

Django patterns

class django_consistency_enforcer.urls.CapturedArg(annotation: object, converter: type | None = attr_dict['converter'].default)

This represents a named captured group in a url pattern and the converter that django will use on this part of the url.

The from_converter classmethod on this class will instantiate this class with the annotations that are match the shape of the object the converter will return.

annotation : object

The annotation associated with the captured arg

converter : type | None

The converter used to transform the string received from the url

classmethod from_converter(converter: type | None) Self

Return an instance given some converter type.

This knows about the built in converter types. And how to get the return annotation from the to_python method for other converters.

Where it’s used it’s possible to instead pass in a subclass that knows about more converters.

protocol django_consistency_enforcer.urls.CapturedArgMaker

Represents a constructor that returns a CapturedArg object.

Classes that implement this protocol must have the following methods / attributes:

__call__(converter: type | None, /) CapturedArg

Call self as a function.

class django_consistency_enforcer.urls.RawPatternPart(groups: int, captured: dict[str, CapturedArg], where: Where, default_arg_names: set[str] = NOTHING)

Represents a single part of a Django url pattern.

captured : dict[str, CapturedArg]

A dictionary of captured args from this pattern

default_arg_names : set[str]

Holds onto the names of any arguments that are provided to the view regardless of the url

classmethod from_pattern(pattern: _PatternType, default_args: dict[str, object], where: Where, *, captured_arg_maker: CapturedArgMaker) Self

Return an instance given some Django RoutePattern/RegexPattern.

groups : int

The number of captured groups represented by this part of the pattern

where : Where

Holds onto information about where this pattern is defined

class django_consistency_enforcer.urls.RawPattern(parts: Sequence[RawPatternPart], callback: Callable[[...], object], view_class: type | None, where: Where)

Represents the parts that make up a single pattern that routes a request to a specific view.

The naming “Raw” is because this object makes no judgement over the specific type of the view class when this is a class based view.

callback : Callable[[...], object]

The function that is called by Django if this route is matched

classmethod from_parts(parts: Sequence[RawPatternPart], *, callback: Callable[[...], object], where: Where) Self

Create an instance given the parts that make up the pattern and the callback it calls into when the route is matched.

parts : Sequence[RawPatternPart]

The different parts that leads to the whole pattern

view_class : type | None

The view class associated with the callback when it’s marked as having a view class

where : Where

Information about where the final part of the pattern is defined

class django_consistency_enforcer.urls.Pattern

All our scenarios are in terms of this interface.

abstract property callback : Callable[[...], object]

The function Django calls when this pattern is matched.

abstractmethod exclude(*, auth_user_model: type) bool

Used by the test runner to skip analysis of this pattern.

abstractmethod exclude_function(*, auth_user_model: type, function: DispatchFunction) bool

Used by the test runner to skip analysis of this function.

abstract property parts : Sequence[RawPatternPart]

The parts that make up the whole pattern.

abstractmethod relevant_functions() Iterator[DispatchFunction]

Return an iterator of the functions to run function scenarios over.

abstract property where : Where

Information about where the final part of the pattern is defined.

class django_consistency_enforcer.urls.ViewPattern(raw_pattern: RawPattern, parts: Sequence[RawPatternPart], callback: Callable[[...], object], view_class: type[T_ViewClass] | None, where: Where)

A concrete implementation of the Pattern interface that is specific to subclasses of django.views.generic.View

callback : Callable[[...], object]

The function Django calls if this pattern is matched

dispatch_function_request_type() object

Return the annotation that should be given to request positional arguments to dispatch functions.

display_view_class(*, indent: str = ' ') str

Return an empty string if we aren’t a method, otherwise return a string representing information about the view class.

exclude(*, auth_user_model: type) bool

By default no pattern is excluded.

exclude_function(*, auth_user_model: type, function: DispatchFunction) bool

By default only functions defined by Django itself is excluded from analysis.

make_dispatch_function(callback: Callable[[...], object], *, positional: Sequence[tuple[str, object]]) DispatchFunction

Used to return a subclass of django_consistency_enforce.urls.DispatchFunction.

Useful if subclasses want to add different implementation details to these objects.

parts : Sequence[RawPatternPart]

The parts that make up the full pattern

raw_pattern : RawPattern

The full raw pattern this represents

relevant_dispatch_functions(*, view_class: type[T_ViewClass], request_type: object) Iterator[DispatchFunction]

Yield the extra functions that represent specific http method types.

So get, post, delete, etc, as defined by http_method_names on the view class.

As well as http_method_not_allowed method.

relevant_functions() Iterator[DispatchFunction]

Yield the dispatch functions that should be passed into function scenarios.

By default, if the callback is not a method, that’s returned and nothing else.

Otherwise we yield methods found on the view class: - setup - dispatch - make_dispatch_function if we are a subclass of generic.RedirectView - Those yield by relevant_dispatch_functions

view_class : type[T_ViewClass] | None

The subclass of django.views.generic.View if this is a method

where : Where

Information of where the final part of this pattern was defined

django_consistency_enforcer.urls.all_django_patterns(resolver: URLResolver, *, captured_arg_maker: CapturedArgMaker = CapturedArg.from_converter, _chain: list[tuple[_PatternType, dict[str, object], Where]] | None = None) Iterator[RawPattern]

This is used to get us every url pattern matched to the callback associated with that url

We can use this to then check that the url patterns and the signatures of the functions on the view match up

django_consistency_enforcer.urls.ensure_raw_pattern_is_generic_view(*, raw_pattern: RawPattern) type[View] | None

A lot of the code here relies on the behaviour of django.views.generic.View and so this helper exists to look at the raw pattern and complain if the view class is not a generic.View subclass when a view_class is associated with the pattern.

This function returns that view class typed such that mypy knows it’s a generic.View subclass

The implemented checks also rely on the behaviour of django.views.generic.View::as_view to guarantee that if there are any named capture groups in the url pattern, then there will be no positional arguments passed into setup/dispatch/get/post/etc

And so it will also complain if there are no named capture groups if the pattern has any captured groups

Errors

This library also provides a container class for holding onto many errors.

class django_consistency_enforcer.errors.InvalidPattern
class django_consistency_enforcer.errors.FoundInvalidPatterns(errors: ErrorContainer)

Used to indicate that invalid patterns were found.

errors : ErrorContainer
class django_consistency_enforcer.errors.ErrorContainer

This holds onto zero or more InvalidPattern errors with a read only interface for accessing them.

add(error: InvalidPattern) None

Add an error to the container.

property by_most_repeated : Iterator[str]

Return an iterator of the stringified representation of the errors held by the container ordered such that the most repeated errors comes before less repeated errors.

property errors : Iterator[InvalidPattern]

This returns the error container as an iterator and is effectively an alias to it’s __iter__ method.