Skip to content

Commit 3212c71

Browse files
committed
perf: some more performance corrections for as_dict
1 parent 9dc690e commit 3212c71

File tree

1,061 files changed

+2156
-2012
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

1,061 files changed

+2156
-2012
lines changed

hatch.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ pre-install-commands = ["install-packages"]
3636
cov = "pytest --cov-report=term-missing --cov-config=pyproject.toml --cov=robotcode --cov=tests {args}"
3737
no-cov = "cov --no-cov {args}"
3838
test = "pytest {args}"
39-
test-reset = "test --regtest-reset"
39+
test-reset = "test --regtest2-reset"
4040
install-bundled-editable = "python ./scripts/install_bundled_editable.py"
4141
create-json-schema = "python ./scripts/create_robot_toml_json_schema.py"
4242
generate-rf-options = "python ./scripts/generate_rf_options.py"

packages/core/src/robotcode/core/dataclasses.py

Lines changed: 114 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import re
99
from typing import (
1010
Any,
11+
Callable,
1112
Dict,
1213
Iterable,
1314
List,
@@ -104,13 +105,24 @@ def _decode_case(cls, s: str) -> str:
104105
return s
105106

106107

108+
__field_name_cache: Dict[Tuple[Type[Any], dataclasses.Field[Any]], str] = {}
109+
__NOT_SET = object()
110+
111+
107112
def encode_case(obj: Any, field: dataclasses.Field) -> str: # type: ignore
108-
alias = field.metadata.get("alias", None)
109-
if alias:
110-
return str(alias)
111-
if hasattr(obj, "_encode_case"):
112-
return str(obj._encode_case(field.name))
113-
return field.name
113+
t = obj if isinstance(obj, type) else type(obj)
114+
name = __field_name_cache.get((t, field), __NOT_SET)
115+
if name is __NOT_SET:
116+
alias = field.metadata.get("alias", None)
117+
if alias:
118+
name = str(alias)
119+
elif hasattr(obj, "_encode_case"):
120+
name = str(obj._encode_case(field.name))
121+
else:
122+
name = field.name
123+
__field_name_cache[(t, field)] = name
124+
125+
return cast(str, name)
114126

115127

116128
def decode_case(type: Type[_T], name: str) -> str:
@@ -354,28 +366,106 @@ def as_dict(
354366
return cast(Dict[str, Any], _as_dict_inner(value, remove_defaults, encode))
355367

356368

357-
def _as_dict_inner(
358-
value: Any,
359-
remove_defaults: bool,
360-
encode: bool = True,
361-
) -> Any:
362-
if dataclasses.is_dataclass(value):
363-
return {
364-
encode_case(value, f) if encode else f.name: _as_dict_inner(getattr(value, f.name), remove_defaults)
365-
for f in dataclasses.fields(value)
366-
if not remove_defaults or getattr(value, f.name) != f.default
367-
}
369+
NONETYPE = type(None)
368370

369-
if isinstance(value, tuple) and hasattr(value, "_fields"):
370-
return [_as_dict_inner(v, remove_defaults) for v in value]
371371

372-
if isinstance(value, (list, tuple)):
373-
return [_as_dict_inner(v, remove_defaults) for v in value]
372+
def _handle_basic_types(value: Any, _remove_defaults: bool, _encode: bool) -> Any:
373+
return value
374374

375-
if isinstance(value, dict):
376-
return {_as_dict_inner(k, remove_defaults): _as_dict_inner(v, remove_defaults) for k, v in value.items()}
377375

378-
return value
376+
__dataclasses_cache: Dict[Type[Any], Tuple[dataclasses.Field[Any], ...]] = {}
377+
378+
379+
def _handle_dataclass(value: Any, remove_defaults: bool, encode: bool) -> Dict[str, Any]:
380+
t = type(value)
381+
fields = __dataclasses_cache.get(t, None)
382+
if fields is None:
383+
fields = dataclasses.fields(value)
384+
__dataclasses_cache[t] = fields
385+
return {
386+
encode_case(t, f) if encode else f.name: _as_dict_inner(getattr(value, f.name), remove_defaults, encode)
387+
for f in fields
388+
if not remove_defaults or getattr(value, f.name) != f.default
389+
}
390+
391+
392+
def _handle_named_tuple(value: Any, remove_defaults: bool, encode: bool) -> List[Any]:
393+
return [_as_dict_inner(v, remove_defaults, encode) for v in value]
394+
395+
396+
def _handle_sequence(value: Any, remove_defaults: bool, encode: bool) -> List[Any]:
397+
return [_as_dict_inner(v, remove_defaults, encode) for v in value]
398+
399+
400+
def _handle_dict(value: Any, remove_defaults: bool, encode: bool) -> Dict[Any, Any]:
401+
return {
402+
_as_dict_inner(k, remove_defaults, encode): _as_dict_inner(v, remove_defaults, encode) for k, v in value.items()
403+
}
404+
405+
406+
def _handle_enum(value: enum.Enum, remove_defaults: bool, encode: bool) -> Any:
407+
return _as_dict_inner(value.value, remove_defaults, encode)
408+
409+
410+
def _handle_unknown_type(value: Any, _remove_defaults: bool, _encode: bool) -> Any:
411+
import warnings
412+
413+
warnings.warn(f"Can't handle type {type(value)} with value {value!r}")
414+
return repr(value)
415+
416+
417+
__handlers: List[Tuple[Callable[[Any], bool], Callable[[Any, bool, bool], Any]]] = [
418+
(
419+
lambda value: type(value) in {int, bool, float, str, NONETYPE},
420+
_handle_basic_types,
421+
),
422+
(
423+
lambda value: dataclasses.is_dataclass(value),
424+
_handle_dataclass,
425+
),
426+
(lambda value: isinstance(value, enum.Enum), _handle_enum),
427+
(
428+
lambda value: (isinstance(value, tuple) and hasattr(value, "_fields")),
429+
_handle_named_tuple,
430+
),
431+
(
432+
lambda value: isinstance(value, (list, tuple, set, frozenset)),
433+
_handle_sequence,
434+
),
435+
(
436+
lambda value: isinstance(value, dict),
437+
_handle_dict,
438+
),
439+
(
440+
lambda _value: True,
441+
_handle_unknown_type,
442+
),
443+
]
444+
445+
__handlers_cache: Dict[Type[Any], Callable[[Any, bool, bool], Any]] = {}
446+
447+
448+
def _as_dict_inner(
449+
value: Any,
450+
remove_defaults: bool,
451+
encode: bool,
452+
) -> Any:
453+
t = type(value)
454+
func = __handlers_cache.get(t, None)
455+
if func is None:
456+
if t in __handlers_cache:
457+
return __handlers_cache[t](value, remove_defaults, encode)
458+
459+
for h in __handlers:
460+
if h[0](value):
461+
__handlers_cache[t] = h[1]
462+
func = h[1]
463+
break
464+
465+
if func is None:
466+
raise TypeError(f"Can't handle type {t} with value {value!r}")
467+
468+
return func(value, remove_defaults, encode)
379469

380470

381471
class TypeValidationError(Exception):

tests/robotcode/language_server/robotframework/parts/_regtest_outputs/rf50/test_code_action_show_documentation.test[code_action_show_documentation.robot-001-016-built-in_library].out

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,5 @@ result:
1414
disabled: null
1515
edit: null
1616
is_preferred: null
17-
kind: !CodeActionKind 'SOURCE'
17+
kind: source
1818
title: Open Documentation

tests/robotcode/language_server/robotframework/parts/_regtest_outputs/rf50/test_code_action_show_documentation.test[code_action_show_documentation.robot-001-021-built-in_library].out

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,5 @@ result:
1414
disabled: null
1515
edit: null
1616
is_preferred: null
17-
kind: !CodeActionKind 'SOURCE'
17+
kind: source
1818
title: Open Documentation

tests/robotcode/language_server/robotframework/parts/_regtest_outputs/rf50/test_code_action_show_documentation.test[code_action_show_documentation.robot-001-026-built-in_library].out

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,5 @@ result:
1414
disabled: null
1515
edit: null
1616
is_preferred: null
17-
kind: !CodeActionKind 'SOURCE'
17+
kind: source
1818
title: Open Documentation

tests/robotcode/language_server/robotframework/parts/_regtest_outputs/rf50/test_code_action_show_documentation.test[code_action_show_documentation.robot-003-016-user_library].out

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,5 @@ result:
1414
disabled: null
1515
edit: null
1616
is_preferred: null
17-
kind: !CodeActionKind 'SOURCE'
17+
kind: source
1818
title: Open Documentation

tests/robotcode/language_server/robotframework/parts/_regtest_outputs/rf50/test_code_action_show_documentation.test[code_action_show_documentation.robot-003-021-user_library].out

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,5 @@ result:
1414
disabled: null
1515
edit: null
1616
is_preferred: null
17-
kind: !CodeActionKind 'SOURCE'
17+
kind: source
1818
title: Open Documentation

tests/robotcode/language_server/robotframework/parts/_regtest_outputs/rf50/test_code_action_show_documentation.test[code_action_show_documentation.robot-003-026-user_library].out

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,5 @@ result:
1414
disabled: null
1515
edit: null
1616
is_preferred: null
17-
kind: !CodeActionKind 'SOURCE'
17+
kind: source
1818
title: Open Documentation

tests/robotcode/language_server/robotframework/parts/_regtest_outputs/rf50/test_code_action_show_documentation.test[code_action_show_documentation.robot-005-016-user_library_by_path_with_variable].out

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,5 @@ result:
1414
disabled: null
1515
edit: null
1616
is_preferred: null
17-
kind: !CodeActionKind 'SOURCE'
17+
kind: source
1818
title: Open Documentation

tests/robotcode/language_server/robotframework/parts/_regtest_outputs/rf50/test_code_action_show_documentation.test[code_action_show_documentation.robot-005-031-user_library_by_path_with_variable].out

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,5 @@ result:
1414
disabled: null
1515
edit: null
1616
is_preferred: null
17-
kind: !CodeActionKind 'SOURCE'
17+
kind: source
1818
title: Open Documentation

0 commit comments

Comments
 (0)