• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

FEniCS / ufl / 18629405325

19 Oct 2025 10:56AM UTC coverage: 77.06% (+0.4%) from 76.622%
18629405325

Pull #401

github

schnellerhase
Ruff
Pull Request #401: Removal of custom type system

494 of 533 new or added lines in 41 files covered. (92.68%)

6 existing lines in 2 files now uncovered.

9325 of 12101 relevant lines covered (77.06%)

0.77 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

89.58
/ufl/corealg/multifunction.py
1
"""Base class for multifunctions with UFL ``Expr`` type dispatch."""
2
# Copyright (C) 2008-2016 Martin Sandve Alnæs
3
#
4
# This file is part of UFL (https://www.fenicsproject.org)
5
#
6
# SPDX-License-Identifier:    LGPL-3.0-or-later
7
#
8
# Modified by Massimiliano Leoni, 2016
9

10
from inspect import signature
1✔
11

12
from ufl.core.ufl_type import UFLRegistry, UFLType
1✔
13

14

15
def get_num_args(function):
1✔
16
    """Return the number of arguments accepted by *function*."""
17
    sig = signature(function)
1✔
18
    return len(sig.parameters) + 1
1✔
19

20

21
def memoized_handler(handler):
1✔
22
    """Function decorator to memoize ``MultiFunction`` handlers."""
23

24
    def _memoized_handler(self, o):
1✔
25
        c = getattr(self, "_memoized_handler_cache")
1✔
26
        r = c.get(o)
1✔
27
        if r is None:
1✔
28
            r = handler(self, o)
1✔
29
            c[o] = r
1✔
30
        return r
1✔
31

32
    return _memoized_handler
1✔
33

34

35
class MultiFunction:
1✔
36
    """Base class for collections of non-recursive expression node handlers.
37

38
    Subclass this (remember to call the ``__init__`` method of this class),
39
    and implement handler functions for each ``Expr`` type, using the lower case
40
    handler name of the type (``exprtype._ufl_handler_name_``).
41

42
    This class is optimized for efficient type based dispatch in the
43
    ``__call__``
44
    operator via typecode based lookup of the handler function bound to the
45
    algorithm object. Of course Python's function call overhead still applies.
46
    """
47

48
    _handlers_cache: dict[type, tuple[list[str], bool]] = {}
1✔
49

50
    def __init__(self):
1✔
51
        """Initialise."""
52
        # Analyse class properties and cache handler data the
53
        # first time this is run for a particular class
54
        # (cached for each algorithm for performance)
55
        algorithm_class = type(self)
1✔
56
        cache_data = MultiFunction._handlers_cache.get(algorithm_class)
1✔
57
        if not cache_data:
1✔
58
            handler_names = [None] * len(UFLRegistry().all_classes)
1✔
59

60
            # Iterate over the inheritance chain for each Expr
61
            # subclass (NB! This assumes that all UFL classes inherits
62
            # from a single Expr subclass and that the first
63
            # superclass is always from the UFL Expr hierarchy!)
64
            for classobject in UFLRegistry().all_classes:
1✔
65
                for c in classobject.mro():
1✔
66
                    # Register classobject with handler for the first
67
                    # encountered superclass
68
                    try:
1✔
69
                        handler_name = c._ufl_handler_name_
1✔
UNCOV
70
                    except AttributeError as attribute_error:
×
UNCOV
71
                        if type(classobject) is not UFLType:
×
72
                            raise attribute_error
×
73
                        # Default handler name for UFL types
UNCOV
74
                        handler_name = UFLType._ufl_handler_name_
×
75

76
                    if hasattr(self, handler_name):
1✔
77
                        handler_names[classobject._ufl_typecode_] = handler_name
1✔
78
                        break
1✔
79
            is_cutoff_type = [get_num_args(getattr(self, name)) == 2 for name in handler_names]
1✔
80
            cache_data = (handler_names, is_cutoff_type)
1✔
81
            MultiFunction._handlers_cache[algorithm_class] = cache_data
1✔
82

83
        # Build handler list for this particular class (get functions
84
        # bound to self, these cannot be cached)
85
        handler_names, is_cutoff_type = cache_data
1✔
86
        self._handlers = [getattr(self, name) for name in handler_names]
1✔
87
        self._is_cutoff_type = is_cutoff_type
1✔
88

89
        # Create cache for memoized_handler
90
        self._memoized_handler_cache = {}
1✔
91

92
    def __call__(self, o, *args):
1✔
93
        """Delegate to handler function based on typecode of first argument."""
94
        return self._handlers[o._ufl_typecode_](o, *args)
1✔
95

96
    def undefined(self, o, *args):
1✔
97
        """Trigger error for types with missing handlers."""
NEW
98
        raise ValueError(f"No handler defined for {type(o).__name__}.")
×
99

100
    def reuse_if_untouched(self, o, *ops):
1✔
101
        """Reuse object if operands are the same objects.
102

103
        Use in your own subclass by setting e.g.
104
        ::
105

106
            expr = MultiFunction.reuse_if_untouched
107

108
        as a default rule.
109
        """
110
        if all(a is b for a, b in zip(o.ufl_operands, ops)):
1✔
111
            return o
1✔
112
        else:
113
            return o._ufl_expr_reconstruct_(*ops)
1✔
114

115
    # Set default behaviour for any UFLType as undefined
116
    ufl_type = undefined
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc