We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Handle optional args in query cost multiplers
Query cost validator supports complexity multipliers which multiply given complexity by sum of specified values:

```graphql
type Query {
    news(promoted: Int, regular: Int): Post @cost(complexity: 1, multipliers: ["promoted", "regular"])
}
```

In situation when one of those arguments is skipped, its passed to complexity calculation as `None`, causing the `unsupported operand type(s) for +: 'int' and 'NoneType'` error within validator.

Relevant lines:

https://github.com/mirumee/ariadne/blob/master/ariadne/validation/query_cost.py#L283
https://github.com/mirumee/ariadne/blob/master/ariadne/validation/query_cost.py#L187

We should default missing variables to 0, so they are filtered out at next line. We should also consider filtering off negative numbers.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `ariadne/validation/query_cost.py`
Content:
```
1 from functools import reduce
2 from operator import add, mul
3 from typing import Any, Dict, List, Optional, Type, Union, cast
4 
5 from graphql import (
6     GraphQLError,
7     GraphQLInterfaceType,
8     GraphQLObjectType,
9     GraphQLSchema,
10     get_named_type,
11 )
12 from graphql.execution.values import get_argument_values
13 from graphql.language import (
14     BooleanValueNode,
15     FieldNode,
16     FragmentDefinitionNode,
17     FragmentSpreadNode,
18     InlineFragmentNode,
19     IntValueNode,
20     ListValueNode,
21     Node,
22     OperationDefinitionNode,
23     OperationType,
24     StringValueNode,
25 )
26 from graphql.type import GraphQLFieldMap
27 from graphql.validation import ValidationContext
28 from graphql.validation.rules import ASTValidationRule, ValidationRule
29 
30 cost_directive = """
31 directive @cost(complexity: Int, multipliers: [String!], useMultipliers: Boolean) on FIELD | FIELD_DEFINITION
32 """
33 
34 
35 CostAwareNode = Union[
36     FieldNode,
37     FragmentDefinitionNode,
38     FragmentSpreadNode,
39     InlineFragmentNode,
40     OperationDefinitionNode,
41 ]
42 
43 
44 class CostValidator(ValidationRule):
45     context: ValidationContext
46     maximum_cost: int
47     default_cost: int = 0
48     default_complexity: int = 1
49     variables: Optional[Dict] = None
50     cost_map: Optional[Dict[str, Dict[str, Any]]] = None
51 
52     def __init__(
53         self,
54         context: ValidationContext,
55         maximum_cost: int,
56         *,
57         default_cost: int = 0,
58         default_complexity: int = 1,
59         variables: Optional[Dict] = None,
60         cost_map: Optional[Dict[str, Dict[str, Any]]] = None,
61     ):  # pylint: disable=super-init-not-called
62         self.context = context
63         self.maximum_cost = maximum_cost
64         self.variables = variables
65         self.cost_map = cost_map
66         self.default_cost = default_cost
67         self.default_complexity = default_complexity
68         self.cost = 0
69         self.operation_multipliers: List[Any] = []
70 
71     def compute_node_cost(self, node: CostAwareNode, type_def, parent_multipliers=None):
72         if parent_multipliers is None:
73             parent_multipliers = []
74         if isinstance(node, FragmentSpreadNode) or not node.selection_set:
75             return 0
76         fields: GraphQLFieldMap = {}
77         if isinstance(type_def, (GraphQLObjectType, GraphQLInterfaceType)):
78             fields = type_def.fields
79         total = 0
80         for child_node in node.selection_set.selections:
81             self.operation_multipliers = parent_multipliers[:]
82             node_cost = self.default_cost
83             if isinstance(child_node, FieldNode):
84                 field = fields.get(child_node.name.value)
85                 if not field:
86                     continue
87                 field_type = get_named_type(field.type)
88                 try:
89                     field_args: Dict[str, Any] = get_argument_values(
90                         field, child_node, self.variables
91                     )
92                 except Exception as e:
93                     report_error(self.context, e)
94                     field_args = {}
95                 if self.cost_map:
96                     cost_map_args = (
97                         self.get_args_from_cost_map(
98                             child_node, type_def.name, field_args
99                         )
100                         if type_def and type_def.name
101                         else None
102                     )
103                     if cost_map_args is not None:
104                         try:
105                             node_cost = self.compute_cost(**cost_map_args)
106                         except (TypeError, ValueError) as e:
107                             report_error(self.context, e)
108                 else:
109                     cost_is_computed = False
110                     if field.ast_node and field.ast_node.directives:
111                         directives_args = self.get_args_from_directives(
112                             field.ast_node.directives, field_args
113                         )
114                         if directives_args is not None:
115                             try:
116                                 node_cost = self.compute_cost(**directives_args)
117                             except (TypeError, ValueError) as e:
118                                 report_error(self.context, e)
119                             cost_is_computed = True
120                     if (
121                         field_type
122                         and field_type.ast_node
123                         and field_type.ast_node.directives
124                     ):
125                         if not cost_is_computed and isinstance(
126                             field_type, GraphQLObjectType
127                         ):
128                             directives_args = self.get_args_from_directives(
129                                 field_type.ast_node.directives, field_args
130                             )
131                             if directives_args is not None:
132                                 try:
133                                     node_cost = self.compute_cost(**directives_args)
134                                 except (TypeError, ValueError) as e:
135                                     report_error(self.context, e)
136                 child_cost = self.compute_node_cost(
137                     child_node, field_type, self.operation_multipliers
138                 )
139                 node_cost += child_cost
140             if isinstance(child_node, FragmentSpreadNode):
141                 fragment = self.context.get_fragment(child_node.name.value)
142                 if fragment:
143                     fragment_type = self.context.schema.get_type(
144                         fragment.type_condition.name.value
145                     )
146                     node_cost = self.compute_node_cost(fragment, fragment_type)
147             if isinstance(child_node, InlineFragmentNode):
148                 inline_fragment_type = type_def
149                 if child_node.type_condition and child_node.type_condition.name:
150                     inline_fragment_type = self.context.schema.get_type(
151                         child_node.type_condition.name.value
152                     )
153                 node_cost = self.compute_node_cost(child_node, inline_fragment_type)
154             total += node_cost
155         return total
156 
157     def enter_operation_definition(
158         self, node, key, parent, path, ancestors
159     ):  # pylint: disable=unused-argument
160         if self.cost_map:
161             try:
162                 validate_cost_map(self.cost_map, self.context.schema)
163             except GraphQLError as cost_map_error:
164                 self.context.report_error(cost_map_error)
165                 return
166 
167         if node.operation is OperationType.QUERY:
168             self.cost += self.compute_node_cost(node, self.context.schema.query_type)
169         if node.operation is OperationType.MUTATION:
170             self.cost += self.compute_node_cost(node, self.context.schema.mutation_type)
171         if node.operation is OperationType.SUBSCRIPTION:
172             self.cost += self.compute_node_cost(
173                 node, self.context.schema.subscription_type
174             )
175 
176     def leave_operation_definition(
177         self, node, key, parent, path, ancestors
178     ):  # pylint: disable=unused-argument
179         if self.cost > self.maximum_cost:
180             self.context.report_error(self.get_cost_exceeded_error())
181 
182     def compute_cost(self, multipliers=None, use_multipliers=True, complexity=None):
183         if complexity is None:
184             complexity = self.default_complexity
185         if use_multipliers:
186             if multipliers:
187                 multiplier = reduce(add, multipliers, 0)
188                 self.operation_multipliers = self.operation_multipliers + [multiplier]
189             return reduce(mul, self.operation_multipliers, complexity)
190         return complexity
191 
192     def get_args_from_cost_map(
193         self, node: FieldNode, parent_type: str, field_args: Dict
194     ):
195         cost_args = None
196         cost_map = cast(Dict[Any, Dict], self.cost_map)
197         if parent_type in cost_map:
198             cost_args = cost_map[parent_type].get(node.name.value)
199         if not cost_args:
200             return None
201         cost_args = cost_args.copy()
202         if "multipliers" in cost_args:
203             cost_args["multipliers"] = self.get_multipliers_from_string(
204                 cost_args["multipliers"], field_args
205             )
206         return cost_args
207 
208     def get_args_from_directives(self, directives, field_args):
209         cost_directive = next(
210             (directive for directive in directives if directive.name.value == "cost"),
211             None,
212         )
213         if cost_directive and cost_directive.arguments:
214             complexity_arg = next(
215                 (
216                     argument
217                     for argument in cost_directive.arguments
218                     if argument.name.value == "complexity"
219                 ),
220                 None,
221             )
222             use_multipliers_arg = next(
223                 (
224                     argument
225                     for argument in cost_directive.arguments
226                     if argument.name.value == "useMultipliers"
227                 ),
228                 None,
229             )
230             multipliers_arg = next(
231                 (
232                     argument
233                     for argument in cost_directive.arguments
234                     if argument.name.value == "multipliers"
235                 ),
236                 None,
237             )
238             use_multipliers = (
239                 use_multipliers_arg.value.value
240                 if use_multipliers_arg
241                 and use_multipliers_arg.value
242                 and isinstance(use_multipliers_arg.value, BooleanValueNode)
243                 else True
244             )
245             multipliers = (
246                 self.get_multipliers_from_list_node(
247                     multipliers_arg.value.values, field_args
248                 )
249                 if multipliers_arg
250                 and multipliers_arg.value
251                 and isinstance(multipliers_arg.value, ListValueNode)
252                 else []
253             )
254             complexity = (
255                 int(complexity_arg.value.value)
256                 if complexity_arg
257                 and complexity_arg.value
258                 and isinstance(complexity_arg.value, IntValueNode)
259                 else None
260             )
261             return {
262                 "complexity": complexity,
263                 "multipliers": multipliers,
264                 "use_multipliers": use_multipliers,
265             }
266 
267         return None
268 
269     def get_multipliers_from_list_node(self, multipliers: List[Node], field_args):
270         multipliers = [
271             node.value  # type: ignore
272             for node in multipliers
273             if isinstance(node, StringValueNode)
274         ]
275         return self.get_multipliers_from_string(multipliers, field_args)  # type: ignore
276 
277     def get_multipliers_from_string(self, multipliers: List[str], field_args):
278         accessors = [s.split(".") for s in multipliers]
279         multipliers = []
280         for accessor in accessors:
281             val = field_args
282             for key in accessor:
283                 val = val.get(key)
284             multipliers.append(val)
285         multipliers = [
286             len(multiplier) if isinstance(multiplier, (list, tuple)) else multiplier
287             for multiplier in multipliers
288         ]
289         return [m for m in multipliers if m != 0]
290 
291     def get_cost_exceeded_error(self) -> GraphQLError:
292         return GraphQLError(
293             cost_analysis_message(self.maximum_cost, self.cost),
294             extensions={
295                 "cost": {
296                     "requestedQueryCost": self.cost,
297                     "maximumAvailable": self.maximum_cost,
298                 }
299             },
300         )
301 
302 
303 def validate_cost_map(cost_map: Dict[str, Dict[str, Any]], schema: GraphQLSchema):
304     for type_name, type_fields in cost_map.items():
305         if type_name not in schema.type_map:
306             raise GraphQLError(
307                 "The query cost could not be calculated because cost map specifies a type "
308                 f"{type_name} that is not defined by the schema."
309             )
310 
311         if not isinstance(schema.type_map[type_name], GraphQLObjectType):
312             raise GraphQLError(
313                 "The query cost could not be calculated because cost map specifies a type "
314                 f"{type_name} that is defined by the schema, but is not an object type."
315             )
316 
317         for field_name in type_fields:
318             graphql_type = cast(GraphQLObjectType, schema.type_map[type_name])
319             if field_name not in graphql_type.fields:
320                 raise GraphQLError(
321                     "The query cost could not be calculated because cost map contains "
322                     f"a field {field_name} not defined by the {type_name} type."
323                 )
324 
325 
326 def report_error(context: ValidationContext, error: Exception):
327     context.report_error(GraphQLError(str(error), original_error=error))
328 
329 
330 def cost_analysis_message(maximum_cost: int, cost: int) -> str:
331     return "The query exceeds the maximum cost of %d. Actual cost is %d" % (
332         maximum_cost,
333         cost,
334     )
335 
336 
337 def cost_validator(
338     maximum_cost: int,
339     *,
340     default_cost: int = 0,
341     default_complexity: int = 1,
342     variables: Optional[Dict] = None,
343     cost_map: Optional[Dict[str, Dict[str, Any]]] = None,
344 ) -> Type[ASTValidationRule]:
345     class _CostValidator(CostValidator):
346         def __init__(self, context: ValidationContext):
347             super().__init__(
348                 context,
349                 maximum_cost=maximum_cost,
350                 default_cost=default_cost,
351                 default_complexity=default_complexity,
352                 variables=variables,
353                 cost_map=cost_map,
354             )
355 
356     return cast(Type[ASTValidationRule], _CostValidator)
357
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/ariadne/validation/query_cost.py b/ariadne/validation/query_cost.py
--- a/ariadne/validation/query_cost.py
+++ b/ariadne/validation/query_cost.py
@@ -281,12 +281,15 @@
             val = field_args
             for key in accessor:
                 val = val.get(key)
-            multipliers.append(val)
+            try:
+                multipliers.append(int(val))  # type: ignore
+            except (ValueError, TypeError):
+                pass
         multipliers = [
             len(multiplier) if isinstance(multiplier, (list, tuple)) else multiplier
             for multiplier in multipliers
         ]
-        return [m for m in multipliers if m != 0]
+        return [m for m in multipliers if m > 0]  # type: ignore
 
     def get_cost_exceeded_error(self) -> GraphQLError:
         return GraphQLError(

{"golden_diff": "diff --git a/ariadne/validation/query_cost.py b/ariadne/validation/query_cost.py\n--- a/ariadne/validation/query_cost.py\n+++ b/ariadne/validation/query_cost.py\n@@ -281,12 +281,15 @@\n             val = field_args\n             for key in accessor:\n                 val = val.get(key)\n-            multipliers.append(val)\n+            try:\n+                multipliers.append(int(val))  # type: ignore\n+            except (ValueError, TypeError):\n+                pass\n         multipliers = [\n             len(multiplier) if isinstance(multiplier, (list, tuple)) else multiplier\n             for multiplier in multipliers\n         ]\n-        return [m for m in multipliers if m != 0]\n+        return [m for m in multipliers if m > 0]  # type: ignore\n \n     def get_cost_exceeded_error(self) -> GraphQLError:\n         return GraphQLError(\n", "issue": "Handle optional args in query cost multiplers\nQuery cost validator supports complexity multipliers which multiply given complexity by sum of specified values:\r\n\r\n```graphql\r\ntype Query {\r\n    news(promoted: Int, regular: Int): Post @cost(complexity: 1, multipliers: [\"promoted\", \"regular\"])\r\n}\r\n```\r\n\r\nIn situation when one of those arguments is skipped, its passed to complexity calculation as `None`, causing the `unsupported operand type(s) for +: 'int' and 'NoneType'` error within validator.\r\n\r\nRelevant lines:\r\n\r\nhttps://github.com/mirumee/ariadne/blob/master/ariadne/validation/query_cost.py#L283\r\nhttps://github.com/mirumee/ariadne/blob/master/ariadne/validation/query_cost.py#L187\r\n\r\nWe should default missing variables to 0, so they are filtered out at next line. We should also consider filtering off negative numbers.\n", "before_files": [{"content": "from functools import reduce\nfrom operator import add, mul\nfrom typing import Any, Dict, List, Optional, Type, Union, cast\n\nfrom graphql import (\n    GraphQLError,\n    GraphQLInterfaceType,\n    GraphQLObjectType,\n    GraphQLSchema,\n    get_named_type,\n)\nfrom graphql.execution.values import get_argument_values\nfrom graphql.language import (\n    BooleanValueNode,\n    FieldNode,\n    FragmentDefinitionNode,\n    FragmentSpreadNode,\n    InlineFragmentNode,\n    IntValueNode,\n    ListValueNode,\n    Node,\n    OperationDefinitionNode,\n    OperationType,\n    StringValueNode,\n)\nfrom graphql.type import GraphQLFieldMap\nfrom graphql.validation import ValidationContext\nfrom graphql.validation.rules import ASTValidationRule, ValidationRule\n\ncost_directive = \"\"\"\ndirective @cost(complexity: Int, multipliers: [String!], useMultipliers: Boolean) on FIELD | FIELD_DEFINITION\n\"\"\"\n\n\nCostAwareNode = Union[\n    FieldNode,\n    FragmentDefinitionNode,\n    FragmentSpreadNode,\n    InlineFragmentNode,\n    OperationDefinitionNode,\n]\n\n\nclass CostValidator(ValidationRule):\n    context: ValidationContext\n    maximum_cost: int\n    default_cost: int = 0\n    default_complexity: int = 1\n    variables: Optional[Dict] = None\n    cost_map: Optional[Dict[str, Dict[str, Any]]] = None\n\n    def __init__(\n        self,\n        context: ValidationContext,\n        maximum_cost: int,\n        *,\n        default_cost: int = 0,\n        default_complexity: int = 1,\n        variables: Optional[Dict] = None,\n        cost_map: Optional[Dict[str, Dict[str, Any]]] = None,\n    ):  # pylint: disable=super-init-not-called\n        self.context = context\n        self.maximum_cost = maximum_cost\n        self.variables = variables\n        self.cost_map = cost_map\n        self.default_cost = default_cost\n        self.default_complexity = default_complexity\n        self.cost = 0\n        self.operation_multipliers: List[Any] = []\n\n    def compute_node_cost(self, node: CostAwareNode, type_def, parent_multipliers=None):\n        if parent_multipliers is None:\n            parent_multipliers = []\n        if isinstance(node, FragmentSpreadNode) or not node.selection_set:\n            return 0\n        fields: GraphQLFieldMap = {}\n        if isinstance(type_def, (GraphQLObjectType, GraphQLInterfaceType)):\n            fields = type_def.fields\n        total = 0\n        for child_node in node.selection_set.selections:\n            self.operation_multipliers = parent_multipliers[:]\n            node_cost = self.default_cost\n            if isinstance(child_node, FieldNode):\n                field = fields.get(child_node.name.value)\n                if not field:\n                    continue\n                field_type = get_named_type(field.type)\n                try:\n                    field_args: Dict[str, Any] = get_argument_values(\n                        field, child_node, self.variables\n                    )\n                except Exception as e:\n                    report_error(self.context, e)\n                    field_args = {}\n                if self.cost_map:\n                    cost_map_args = (\n                        self.get_args_from_cost_map(\n                            child_node, type_def.name, field_args\n                        )\n                        if type_def and type_def.name\n                        else None\n                    )\n                    if cost_map_args is not None:\n                        try:\n                            node_cost = self.compute_cost(**cost_map_args)\n                        except (TypeError, ValueError) as e:\n                            report_error(self.context, e)\n                else:\n                    cost_is_computed = False\n                    if field.ast_node and field.ast_node.directives:\n                        directives_args = self.get_args_from_directives(\n                            field.ast_node.directives, field_args\n                        )\n                        if directives_args is not None:\n                            try:\n                                node_cost = self.compute_cost(**directives_args)\n                            except (TypeError, ValueError) as e:\n                                report_error(self.context, e)\n                            cost_is_computed = True\n                    if (\n                        field_type\n                        and field_type.ast_node\n                        and field_type.ast_node.directives\n                    ):\n                        if not cost_is_computed and isinstance(\n                            field_type, GraphQLObjectType\n                        ):\n                            directives_args = self.get_args_from_directives(\n                                field_type.ast_node.directives, field_args\n                            )\n                            if directives_args is not None:\n                                try:\n                                    node_cost = self.compute_cost(**directives_args)\n                                except (TypeError, ValueError) as e:\n                                    report_error(self.context, e)\n                child_cost = self.compute_node_cost(\n                    child_node, field_type, self.operation_multipliers\n                )\n                node_cost += child_cost\n            if isinstance(child_node, FragmentSpreadNode):\n                fragment = self.context.get_fragment(child_node.name.value)\n                if fragment:\n                    fragment_type = self.context.schema.get_type(\n                        fragment.type_condition.name.value\n                    )\n                    node_cost = self.compute_node_cost(fragment, fragment_type)\n            if isinstance(child_node, InlineFragmentNode):\n                inline_fragment_type = type_def\n                if child_node.type_condition and child_node.type_condition.name:\n                    inline_fragment_type = self.context.schema.get_type(\n                        child_node.type_condition.name.value\n                    )\n                node_cost = self.compute_node_cost(child_node, inline_fragment_type)\n            total += node_cost\n        return total\n\n    def enter_operation_definition(\n        self, node, key, parent, path, ancestors\n    ):  # pylint: disable=unused-argument\n        if self.cost_map:\n            try:\n                validate_cost_map(self.cost_map, self.context.schema)\n            except GraphQLError as cost_map_error:\n                self.context.report_error(cost_map_error)\n                return\n\n        if node.operation is OperationType.QUERY:\n            self.cost += self.compute_node_cost(node, self.context.schema.query_type)\n        if node.operation is OperationType.MUTATION:\n            self.cost += self.compute_node_cost(node, self.context.schema.mutation_type)\n        if node.operation is OperationType.SUBSCRIPTION:\n            self.cost += self.compute_node_cost(\n                node, self.context.schema.subscription_type\n            )\n\n    def leave_operation_definition(\n        self, node, key, parent, path, ancestors\n    ):  # pylint: disable=unused-argument\n        if self.cost > self.maximum_cost:\n            self.context.report_error(self.get_cost_exceeded_error())\n\n    def compute_cost(self, multipliers=None, use_multipliers=True, complexity=None):\n        if complexity is None:\n            complexity = self.default_complexity\n        if use_multipliers:\n            if multipliers:\n                multiplier = reduce(add, multipliers, 0)\n                self.operation_multipliers = self.operation_multipliers + [multiplier]\n            return reduce(mul, self.operation_multipliers, complexity)\n        return complexity\n\n    def get_args_from_cost_map(\n        self, node: FieldNode, parent_type: str, field_args: Dict\n    ):\n        cost_args = None\n        cost_map = cast(Dict[Any, Dict], self.cost_map)\n        if parent_type in cost_map:\n            cost_args = cost_map[parent_type].get(node.name.value)\n        if not cost_args:\n            return None\n        cost_args = cost_args.copy()\n        if \"multipliers\" in cost_args:\n            cost_args[\"multipliers\"] = self.get_multipliers_from_string(\n                cost_args[\"multipliers\"], field_args\n            )\n        return cost_args\n\n    def get_args_from_directives(self, directives, field_args):\n        cost_directive = next(\n            (directive for directive in directives if directive.name.value == \"cost\"),\n            None,\n        )\n        if cost_directive and cost_directive.arguments:\n            complexity_arg = next(\n                (\n                    argument\n                    for argument in cost_directive.arguments\n                    if argument.name.value == \"complexity\"\n                ),\n                None,\n            )\n            use_multipliers_arg = next(\n                (\n                    argument\n                    for argument in cost_directive.arguments\n                    if argument.name.value == \"useMultipliers\"\n                ),\n                None,\n            )\n            multipliers_arg = next(\n                (\n                    argument\n                    for argument in cost_directive.arguments\n                    if argument.name.value == \"multipliers\"\n                ),\n                None,\n            )\n            use_multipliers = (\n                use_multipliers_arg.value.value\n                if use_multipliers_arg\n                and use_multipliers_arg.value\n                and isinstance(use_multipliers_arg.value, BooleanValueNode)\n                else True\n            )\n            multipliers = (\n                self.get_multipliers_from_list_node(\n                    multipliers_arg.value.values, field_args\n                )\n                if multipliers_arg\n                and multipliers_arg.value\n                and isinstance(multipliers_arg.value, ListValueNode)\n                else []\n            )\n            complexity = (\n                int(complexity_arg.value.value)\n                if complexity_arg\n                and complexity_arg.value\n                and isinstance(complexity_arg.value, IntValueNode)\n                else None\n            )\n            return {\n                \"complexity\": complexity,\n                \"multipliers\": multipliers,\n                \"use_multipliers\": use_multipliers,\n            }\n\n        return None\n\n    def get_multipliers_from_list_node(self, multipliers: List[Node], field_args):\n        multipliers = [\n            node.value  # type: ignore\n            for node in multipliers\n            if isinstance(node, StringValueNode)\n        ]\n        return self.get_multipliers_from_string(multipliers, field_args)  # type: ignore\n\n    def get_multipliers_from_string(self, multipliers: List[str], field_args):\n        accessors = [s.split(\".\") for s in multipliers]\n        multipliers = []\n        for accessor in accessors:\n            val = field_args\n            for key in accessor:\n                val = val.get(key)\n            multipliers.append(val)\n        multipliers = [\n            len(multiplier) if isinstance(multiplier, (list, tuple)) else multiplier\n            for multiplier in multipliers\n        ]\n        return [m for m in multipliers if m != 0]\n\n    def get_cost_exceeded_error(self) -> GraphQLError:\n        return GraphQLError(\n            cost_analysis_message(self.maximum_cost, self.cost),\n            extensions={\n                \"cost\": {\n                    \"requestedQueryCost\": self.cost,\n                    \"maximumAvailable\": self.maximum_cost,\n                }\n            },\n        )\n\n\ndef validate_cost_map(cost_map: Dict[str, Dict[str, Any]], schema: GraphQLSchema):\n    for type_name, type_fields in cost_map.items():\n        if type_name not in schema.type_map:\n            raise GraphQLError(\n                \"The query cost could not be calculated because cost map specifies a type \"\n                f\"{type_name} that is not defined by the schema.\"\n            )\n\n        if not isinstance(schema.type_map[type_name], GraphQLObjectType):\n            raise GraphQLError(\n                \"The query cost could not be calculated because cost map specifies a type \"\n                f\"{type_name} that is defined by the schema, but is not an object type.\"\n            )\n\n        for field_name in type_fields:\n            graphql_type = cast(GraphQLObjectType, schema.type_map[type_name])\n            if field_name not in graphql_type.fields:\n                raise GraphQLError(\n                    \"The query cost could not be calculated because cost map contains \"\n                    f\"a field {field_name} not defined by the {type_name} type.\"\n                )\n\n\ndef report_error(context: ValidationContext, error: Exception):\n    context.report_error(GraphQLError(str(error), original_error=error))\n\n\ndef cost_analysis_message(maximum_cost: int, cost: int) -> str:\n    return \"The query exceeds the maximum cost of %d. Actual cost is %d\" % (\n        maximum_cost,\n        cost,\n    )\n\n\ndef cost_validator(\n    maximum_cost: int,\n    *,\n    default_cost: int = 0,\n    default_complexity: int = 1,\n    variables: Optional[Dict] = None,\n    cost_map: Optional[Dict[str, Dict[str, Any]]] = None,\n) -> Type[ASTValidationRule]:\n    class _CostValidator(CostValidator):\n        def __init__(self, context: ValidationContext):\n            super().__init__(\n                context,\n                maximum_cost=maximum_cost,\n                default_cost=default_cost,\n                default_complexity=default_complexity,\n                variables=variables,\n                cost_map=cost_map,\n            )\n\n    return cast(Type[ASTValidationRule], _CostValidator)\n", "path": "ariadne/validation/query_cost.py"}], "after_files": [{"content": "from functools import reduce\nfrom operator import add, mul\nfrom typing import Any, Dict, List, Optional, Type, Union, cast\n\nfrom graphql import (\n    GraphQLError,\n    GraphQLInterfaceType,\n    GraphQLObjectType,\n    GraphQLSchema,\n    get_named_type,\n)\nfrom graphql.execution.values import get_argument_values\nfrom graphql.language import (\n    BooleanValueNode,\n    FieldNode,\n    FragmentDefinitionNode,\n    FragmentSpreadNode,\n    InlineFragmentNode,\n    IntValueNode,\n    ListValueNode,\n    Node,\n    OperationDefinitionNode,\n    OperationType,\n    StringValueNode,\n)\nfrom graphql.type import GraphQLFieldMap\nfrom graphql.validation import ValidationContext\nfrom graphql.validation.rules import ASTValidationRule, ValidationRule\n\ncost_directive = \"\"\"\ndirective @cost(complexity: Int, multipliers: [String!], useMultipliers: Boolean) on FIELD | FIELD_DEFINITION\n\"\"\"\n\n\nCostAwareNode = Union[\n    FieldNode,\n    FragmentDefinitionNode,\n    FragmentSpreadNode,\n    InlineFragmentNode,\n    OperationDefinitionNode,\n]\n\n\nclass CostValidator(ValidationRule):\n    context: ValidationContext\n    maximum_cost: int\n    default_cost: int = 0\n    default_complexity: int = 1\n    variables: Optional[Dict] = None\n    cost_map: Optional[Dict[str, Dict[str, Any]]] = None\n\n    def __init__(\n        self,\n        context: ValidationContext,\n        maximum_cost: int,\n        *,\n        default_cost: int = 0,\n        default_complexity: int = 1,\n        variables: Optional[Dict] = None,\n        cost_map: Optional[Dict[str, Dict[str, Any]]] = None,\n    ):  # pylint: disable=super-init-not-called\n        self.context = context\n        self.maximum_cost = maximum_cost\n        self.variables = variables\n        self.cost_map = cost_map\n        self.default_cost = default_cost\n        self.default_complexity = default_complexity\n        self.cost = 0\n        self.operation_multipliers: List[Any] = []\n\n    def compute_node_cost(self, node: CostAwareNode, type_def, parent_multipliers=None):\n        if parent_multipliers is None:\n            parent_multipliers = []\n        if isinstance(node, FragmentSpreadNode) or not node.selection_set:\n            return 0\n        fields: GraphQLFieldMap = {}\n        if isinstance(type_def, (GraphQLObjectType, GraphQLInterfaceType)):\n            fields = type_def.fields\n        total = 0\n        for child_node in node.selection_set.selections:\n            self.operation_multipliers = parent_multipliers[:]\n            node_cost = self.default_cost\n            if isinstance(child_node, FieldNode):\n                field = fields.get(child_node.name.value)\n                if not field:\n                    continue\n                field_type = get_named_type(field.type)\n                try:\n                    field_args: Dict[str, Any] = get_argument_values(\n                        field, child_node, self.variables\n                    )\n                except Exception as e:\n                    report_error(self.context, e)\n                    field_args = {}\n                if self.cost_map:\n                    cost_map_args = (\n                        self.get_args_from_cost_map(\n                            child_node, type_def.name, field_args\n                        )\n                        if type_def and type_def.name\n                        else None\n                    )\n                    if cost_map_args is not None:\n                        try:\n                            node_cost = self.compute_cost(**cost_map_args)\n                        except (TypeError, ValueError) as e:\n                            report_error(self.context, e)\n                else:\n                    cost_is_computed = False\n                    if field.ast_node and field.ast_node.directives:\n                        directives_args = self.get_args_from_directives(\n                            field.ast_node.directives, field_args\n                        )\n                        if directives_args is not None:\n                            try:\n                                node_cost = self.compute_cost(**directives_args)\n                            except (TypeError, ValueError) as e:\n                                report_error(self.context, e)\n                            cost_is_computed = True\n                    if (\n                        field_type\n                        and field_type.ast_node\n                        and field_type.ast_node.directives\n                    ):\n                        if not cost_is_computed and isinstance(\n                            field_type, GraphQLObjectType\n                        ):\n                            directives_args = self.get_args_from_directives(\n                                field_type.ast_node.directives, field_args\n                            )\n                            if directives_args is not None:\n                                try:\n                                    node_cost = self.compute_cost(**directives_args)\n                                except (TypeError, ValueError) as e:\n                                    report_error(self.context, e)\n                child_cost = self.compute_node_cost(\n                    child_node, field_type, self.operation_multipliers\n                )\n                node_cost += child_cost\n            if isinstance(child_node, FragmentSpreadNode):\n                fragment = self.context.get_fragment(child_node.name.value)\n                if fragment:\n                    fragment_type = self.context.schema.get_type(\n                        fragment.type_condition.name.value\n                    )\n                    node_cost = self.compute_node_cost(fragment, fragment_type)\n            if isinstance(child_node, InlineFragmentNode):\n                inline_fragment_type = type_def\n                if child_node.type_condition and child_node.type_condition.name:\n                    inline_fragment_type = self.context.schema.get_type(\n                        child_node.type_condition.name.value\n                    )\n                node_cost = self.compute_node_cost(child_node, inline_fragment_type)\n            total += node_cost\n        return total\n\n    def enter_operation_definition(\n        self, node, key, parent, path, ancestors\n    ):  # pylint: disable=unused-argument\n        if self.cost_map:\n            try:\n                validate_cost_map(self.cost_map, self.context.schema)\n            except GraphQLError as cost_map_error:\n                self.context.report_error(cost_map_error)\n                return\n\n        if node.operation is OperationType.QUERY:\n            self.cost += self.compute_node_cost(node, self.context.schema.query_type)\n        if node.operation is OperationType.MUTATION:\n            self.cost += self.compute_node_cost(node, self.context.schema.mutation_type)\n        if node.operation is OperationType.SUBSCRIPTION:\n            self.cost += self.compute_node_cost(\n                node, self.context.schema.subscription_type\n            )\n\n    def leave_operation_definition(\n        self, node, key, parent, path, ancestors\n    ):  # pylint: disable=unused-argument\n        if self.cost > self.maximum_cost:\n            self.context.report_error(self.get_cost_exceeded_error())\n\n    def compute_cost(self, multipliers=None, use_multipliers=True, complexity=None):\n        if complexity is None:\n            complexity = self.default_complexity\n        if use_multipliers:\n            if multipliers:\n                multiplier = reduce(add, multipliers, 0)\n                self.operation_multipliers = self.operation_multipliers + [multiplier]\n            return reduce(mul, self.operation_multipliers, complexity)\n        return complexity\n\n    def get_args_from_cost_map(\n        self, node: FieldNode, parent_type: str, field_args: Dict\n    ):\n        cost_args = None\n        cost_map = cast(Dict[Any, Dict], self.cost_map)\n        if parent_type in cost_map:\n            cost_args = cost_map[parent_type].get(node.name.value)\n        if not cost_args:\n            return None\n        cost_args = cost_args.copy()\n        if \"multipliers\" in cost_args:\n            cost_args[\"multipliers\"] = self.get_multipliers_from_string(\n                cost_args[\"multipliers\"], field_args\n            )\n        return cost_args\n\n    def get_args_from_directives(self, directives, field_args):\n        cost_directive = next(\n            (directive for directive in directives if directive.name.value == \"cost\"),\n            None,\n        )\n        if cost_directive and cost_directive.arguments:\n            complexity_arg = next(\n                (\n                    argument\n                    for argument in cost_directive.arguments\n                    if argument.name.value == \"complexity\"\n                ),\n                None,\n            )\n            use_multipliers_arg = next(\n                (\n                    argument\n                    for argument in cost_directive.arguments\n                    if argument.name.value == \"useMultipliers\"\n                ),\n                None,\n            )\n            multipliers_arg = next(\n                (\n                    argument\n                    for argument in cost_directive.arguments\n                    if argument.name.value == \"multipliers\"\n                ),\n                None,\n            )\n            use_multipliers = (\n                use_multipliers_arg.value.value\n                if use_multipliers_arg\n                and use_multipliers_arg.value\n                and isinstance(use_multipliers_arg.value, BooleanValueNode)\n                else True\n            )\n            multipliers = (\n                self.get_multipliers_from_list_node(\n                    multipliers_arg.value.values, field_args\n                )\n                if multipliers_arg\n                and multipliers_arg.value\n                and isinstance(multipliers_arg.value, ListValueNode)\n                else []\n            )\n            complexity = (\n                int(complexity_arg.value.value)\n                if complexity_arg\n                and complexity_arg.value\n                and isinstance(complexity_arg.value, IntValueNode)\n                else None\n            )\n            return {\n                \"complexity\": complexity,\n                \"multipliers\": multipliers,\n                \"use_multipliers\": use_multipliers,\n            }\n\n        return None\n\n    def get_multipliers_from_list_node(self, multipliers: List[Node], field_args):\n        multipliers = [\n            node.value  # type: ignore\n            for node in multipliers\n            if isinstance(node, StringValueNode)\n        ]\n        return self.get_multipliers_from_string(multipliers, field_args)  # type: ignore\n\n    def get_multipliers_from_string(self, multipliers: List[str], field_args):\n        accessors = [s.split(\".\") for s in multipliers]\n        multipliers = []\n        for accessor in accessors:\n            val = field_args\n            for key in accessor:\n                val = val.get(key)\n            try:\n                multipliers.append(int(val))  # type: ignore\n            except (ValueError, TypeError):\n                pass\n        multipliers = [\n            len(multiplier) if isinstance(multiplier, (list, tuple)) else multiplier\n            for multiplier in multipliers\n        ]\n        return [m for m in multipliers if m > 0]  # type: ignore\n\n    def get_cost_exceeded_error(self) -> GraphQLError:\n        return GraphQLError(\n            cost_analysis_message(self.maximum_cost, self.cost),\n            extensions={\n                \"cost\": {\n                    \"requestedQueryCost\": self.cost,\n                    \"maximumAvailable\": self.maximum_cost,\n                }\n            },\n        )\n\n\ndef validate_cost_map(cost_map: Dict[str, Dict[str, Any]], schema: GraphQLSchema):\n    for type_name, type_fields in cost_map.items():\n        if type_name not in schema.type_map:\n            raise GraphQLError(\n                \"The query cost could not be calculated because cost map specifies a type \"\n                f\"{type_name} that is not defined by the schema.\"\n            )\n\n        if not isinstance(schema.type_map[type_name], GraphQLObjectType):\n            raise GraphQLError(\n                \"The query cost could not be calculated because cost map specifies a type \"\n                f\"{type_name} that is defined by the schema, but is not an object type.\"\n            )\n\n        for field_name in type_fields:\n            graphql_type = cast(GraphQLObjectType, schema.type_map[type_name])\n            if field_name not in graphql_type.fields:\n                raise GraphQLError(\n                    \"The query cost could not be calculated because cost map contains \"\n                    f\"a field {field_name} not defined by the {type_name} type.\"\n                )\n\n\ndef report_error(context: ValidationContext, error: Exception):\n    context.report_error(GraphQLError(str(error), original_error=error))\n\n\ndef cost_analysis_message(maximum_cost: int, cost: int) -> str:\n    return \"The query exceeds the maximum cost of %d. Actual cost is %d\" % (\n        maximum_cost,\n        cost,\n    )\n\n\ndef cost_validator(\n    maximum_cost: int,\n    *,\n    default_cost: int = 0,\n    default_complexity: int = 1,\n    variables: Optional[Dict] = None,\n    cost_map: Optional[Dict[str, Dict[str, Any]]] = None,\n) -> Type[ASTValidationRule]:\n    class _CostValidator(CostValidator):\n        def __init__(self, context: ValidationContext):\n            super().__init__(\n                context,\n                maximum_cost=maximum_cost,\n                default_cost=default_cost,\n                default_complexity=default_complexity,\n                variables=variables,\n                cost_map=cost_map,\n            )\n\n    return cast(Type[ASTValidationRule], _CostValidator)\n", "path": "ariadne/validation/query_cost.py"}]}