Python Classes Without Boilerplate
This report summarizes scanner evidence for python-attrs/attrs, a Python project with 5,852 GitHub stars. SynthScan examined 28,647 lines of code across 102 source files, recording 359 pattern matches distributed across 13 syntactic categories. The legacy adjusted pattern signal is 12.8 (Moderate pattern signal); it is not an authorship estimate.
Use the findings to ask whether maintainers can explain intent, verify dependencies, simplify unnecessary scaffolding, and focus review effort. Risk Payload v1 adds ownership, supply-chain, over-engineering, and review-effort vectors when available. These are prompts for human verification, not probabilities, security findings, or measured maintenance costs.
Use each vector to guide manual verification of the underlying findings.
Can maintainers explain intent, assumptions, and trade-offs? 285 finding(s).
Are imports declared and package/API names verified? 65 finding(s).
Are wrappers, guards, and repeated structures necessary? 265 finding(s).
Which changes need focused human review? 359 finding(s).
This chart tracks the legacy adjusted pattern score across scan runs. Changes can reflect source changes, scan coverage, or detector/scoring versions; they do not measure AI adoption or prove that a team refactored code. Compare runs only when their scoring versions are compatible. The dashed line tracks the raw pattern-hit count.
Groups findings by configured severity weight. Severity is not a confidence estimate, security rating, or proof that a package is missing. Verify imports against the project's manifests and inspect each finding in its local design context.
This horizontal bar chart shows where configured pattern scores accumulate across directories. Treat the bars as locations to inspect, not as evidence of authorship or code quality. Use the file-level findings to decide whether a directory needs a focused ownership, dependency, complexity, or review check.
The scanner identified 359 distinct pattern matches across 13 syntactic categories. Each entry below represents a discrete location in the source code where the engine recorded a detector match to inspect in context. Expand any category row to inspect the individual file paths, line numbers, code snippets, and the lexical context (CODE, COMMENT, or STRING) in which each match was detected.
Reading the findings table: The Severity column indicates the configured finding weight (CRITICAL / HIGH / MEDIUM / LOW). The Context column identifies whether the match occurred inside executable code, an inline comment, or a string literal — legacy comment-context matches receive a ×1.5 score weight. The ⚡ bolt icon marks clustered matches: three or more patterns within a 10-line window, each receiving an additional ×1.5 density multiplier under the legacy scoring model.
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | bench/test_benchmarks.py | 48 | def test_create_simple_class_make_class(): | CODE |
| LOW | bench/test_benchmarks.py | 72 | def test_instantiate_no_defaults(): | CODE |
| LOW | bench/test_benchmarks.py | 80 | def test_instantiate_with_defaults(): | CODE |
| LOW | bench/test_benchmarks.py | 218 | def test_create_cached_property_class(self): | CODE |
| LOW | tests/test_pyright.py | 89 | def test_pyright_attrsinstance_compat(tmp_path): | CODE |
| LOW | tests/test_pyright.py | 117 | def test_pyright_field_converters_tuple(tmp_path): | CODE |
| LOW | tests/test_funcs.py | 160 | def test_lists_tuples_retain_type(self, container, C): | CODE |
| LOW | tests/test_funcs.py | 214 | def test_asdict_preserve_order(self, cls): | CODE |
| LOW | tests/test_funcs.py | 223 | def test_retain_keys_are_tuples(self): | CODE |
| LOW | tests/test_funcs.py | 254 | def test_named_tuple_retain_type(self): | CODE |
| LOW | tests/test_funcs.py | 275 | def test_type_error_with_retain_type(self): | CODE |
| LOW | tests/test_funcs.py | 332 | def assert_proper_tuple_class(obj, obj_tuple): | CODE |
| LOW | tests/test_funcs.py | 413 | def test_lists_tuples_retain_type(self, container, C): | CODE |
| LOW | tests/test_funcs.py | 460 | def test_named_tuple_retain_type(self): | CODE |
| LOW | tests/test_funcs.py | 481 | def test_type_error_with_retain_type(self): | CODE |
| LOW | tests/test_funcs.py | 773 | def test_regression_attrs_classes(self): | CODE |
| LOW | tests/test_forward_references.py | 8 | def test_forward_class_reference(): | CODE |
| LOW | tests/test_utils.py | 15 | def test_returns_distinct_classes(self): | CODE |
| LOW | tests/test_converters.py | 95 | def test_annotations_if_last_in_pipe(self): | CODE |
| LOW | tests/strategies.py | 52 | def _create_hyp_nested_strategy(draw, simple_class_strategy): | CODE |
| LOW | tests/strategies.py | 90 | def simple_attrs_with_metadata(draw): | CODE |
| LOW | tests/test_functional.py | 225 | def test_subclassing_with_extra_attrs(self, cls): | CODE |
| LOW | tests/test_functional.py | 240 | def test_subclass_without_extra_attrs(self, base): | CODE |
| LOW | tests/test_functional.py | 332 | def test_subclassing_frozen_gives_frozen(self): | CODE |
| LOW | tests/test_functional.py | 420 | def test_handles_different_defaults(self): | CODE |
| LOW | tests/test_functional.py | 437 | def test_unsafe_hash_false_eq_false(self, slots): | CODE |
| LOW | tests/test_functional.py | 545 | def test_tuple_class_aliasing(self): | CODE |
| LOW | tests/test_functional.py | 660 | def test_no_setattr_if_validate_without_validators(self, slots): | CODE |
| LOW | tests/test_functional.py | 684 | def test_no_setattr_if_convert_without_converters(self, slots): | CODE |
| LOW | tests/test_functional.py | 705 | def test_no_setattr_with_ng_defaults(self, slots): | CODE |
| LOW | tests/test_functional.py | 733 | def test_on_setattr_detect_inherited_validators(self): | CODE |
| LOW | tests/test_setattr.py | 51 | def test_change_with_generator_hook(self): | CODE |
| LOW | tests/test_setattr.py | 71 | def test_generator_hook_executes_after_yielding(self): | CODE |
| LOW | tests/test_setattr.py | 194 | def test_generator_hook_not_called_in_init(self): | CODE |
| LOW | tests/test_setattr.py | 214 | def test_generator_hook_with_define(self): | CODE |
| LOW | tests/test_setattr.py | 234 | def test_generator_hook_class_level(self): | CODE |
| LOW | tests/test_setattr.py | 262 | def test_generator_pre_yield_exception(self): | CODE |
| LOW | tests/test_setattr.py | 282 | def test_generator_post_yield_exception(self): | CODE |
| LOW | tests/test_setattr.py | 303 | def test_generator_yields_multiple_times(self): | CODE |
| LOW | tests/test_setattr.py | 344 | def test_no_validator_no_converter(self): | CODE |
| LOW | tests/test_setattr.py | 359 | def test_validate_respects_run_validators_config(self): | CODE |
| LOW | tests/test_setattr.py | 383 | def test_frozen_on_setattr_class_is_caught(self): | CODE |
| LOW | tests/test_setattr.py | 395 | def test_frozen_on_setattr_attribute_is_caught(self): | CODE |
| LOW | tests/test_setattr.py | 409 | def test_frozen_on_setattr_nops(self, nop): | CODE |
| LOW | tests/test_setattr.py | 418 | def test_setattr_reset_if_no_custom_setattr(self, slots): | CODE |
| LOW | tests/test_setattr.py | 443 | def test_setattr_inherited_do_not_reset(self, slots): | CODE |
| LOW | tests/test_setattr.py | 470 | def test_pickling_retains_attrs_own(self, slots): | CODE |
| LOW | tests/test_setattr.py | 487 | def test_slotted_class_can_have_custom_setattr(self): | CODE |
| LOW | tests/test_setattr.py | 526 | def test_setattr_auto_detect_if_no_custom_setattr(self, slots): | CODE |
| LOW | tests/test_setattr.py | 545 | def test_setattr_restore_respects_auto_detect(self, slots): | CODE |
| LOW | tests/test_setattr.py | 558 | def test_setattr_auto_detect_frozen(self, slots): | CODE |
| LOW | tests/test_setattr.py | 571 | def test_setattr_auto_detect_on_setattr(self, slots): | CODE |
| LOW | tests/test_setattr.py | 591 | def test_setattr_inherited_do_not_reset_intermediate( | CODE |
| LOW | tests/test_setattr.py | 633 | def test_setattr_converter_piped(self): | CODE |
| LOW⚡ | tests/test_make.py | 2654 | def test_eq_callable_order_boolean(self): | CODE |
| LOW⚡ | tests/test_make.py | 2663 | def test_eq_callable_order_callable(self): | CODE |
| LOW⚡ | tests/test_make.py | 2672 | def test_eq_boolean_order_callable(self): | CODE |
| LOW⚡ | tests/test_make.py | 2682 | def test_order_mirrors_eq_by_default(self, eq): | CODE |
| LOW | tests/test_make.py | 67 | def _with_and_without_validation(request): | CODE |
| LOW | tests/test_make.py | 92 | def test_validators_lists_to_wrapped_tuples(self): | CODE |
| 189 more matches not shown… | ||||
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | bench/test_benchmarks.py | 5 | CODE | |
| LOW | typing_tests/baseline.py | 8 | CODE | |
| LOW | typing_tests/pyrefly.py | 7 | CODE | |
| LOW | typing_tests/mypy.py | 7 | CODE | |
| LOW | tests/test_pyright.py | 3 | CODE | |
| LOW | tests/attr_import_star.py | 4 | CODE | |
| LOW | tests/test_import.py | 14 | CODE | |
| LOW | tests/test_hooks.py | 3 | CODE | |
| LOW | src/attrs/setters.py | 3 | CODE | |
| LOW | src/attrs/validators.py | 3 | CODE | |
| LOW | src/attrs/__init__.py | 3 | CODE | |
| LOW | src/attrs/__init__.py | 3 | CODE | |
| LOW | src/attrs/__init__.py | 3 | CODE | |
| LOW | src/attrs/__init__.py | 3 | CODE | |
| LOW | src/attrs/__init__.py | 3 | CODE | |
| LOW | src/attrs/__init__.py | 3 | CODE | |
| LOW | src/attrs/__init__.py | 3 | CODE | |
| LOW | src/attrs/__init__.py | 3 | CODE | |
| LOW | src/attrs/__init__.py | 3 | CODE | |
| LOW | src/attrs/__init__.py | 3 | CODE | |
| LOW | src/attrs/__init__.py | 3 | CODE | |
| LOW | src/attrs/__init__.py | 3 | CODE | |
| LOW | src/attrs/__init__.py | 3 | CODE | |
| LOW | src/attrs/__init__.py | 3 | CODE | |
| LOW | src/attrs/__init__.py | 3 | CODE | |
| LOW | src/attrs/__init__.py | 3 | CODE | |
| LOW | src/attrs/__init__.py | 3 | CODE | |
| LOW | src/attrs/__init__.py | 3 | CODE | |
| LOW | src/attrs/__init__.py | 3 | CODE | |
| LOW | src/attrs/__init__.py | 25 | CODE | |
| LOW | src/attrs/__init__.py | 26 | CODE | |
| LOW | src/attrs/__init__.py | 26 | CODE | |
| LOW | src/attrs/__init__.py | 26 | CODE | |
| LOW | src/attrs/__init__.py | 28 | CODE | |
| LOW | src/attrs/__init__.py | 28 | CODE | |
| LOW | src/attrs/__init__.py | 28 | CODE | |
| LOW | src/attrs/exceptions.py | 3 | CODE | |
| LOW | src/attrs/converters.py | 3 | CODE | |
| LOW | src/attrs/filters.py | 3 | CODE | |
| LOW | src/attr/__init__.py | 12 | CODE | |
| LOW | src/attr/__init__.py | 12 | CODE | |
| LOW | src/attr/__init__.py | 13 | CODE | |
| LOW | src/attr/__init__.py | 14 | CODE | |
| LOW | src/attr/__init__.py | 14 | CODE | |
| LOW | src/attr/__init__.py | 15 | CODE | |
| LOW | src/attr/__init__.py | 15 | CODE | |
| LOW | src/attr/__init__.py | 15 | CODE | |
| LOW | src/attr/__init__.py | 15 | CODE | |
| LOW | src/attr/__init__.py | 15 | CODE | |
| LOW | src/attr/__init__.py | 16 | CODE | |
| LOW | src/attr/__init__.py | 16 | CODE | |
| LOW | src/attr/__init__.py | 16 | CODE | |
| LOW | src/attr/__init__.py | 16 | CODE | |
| LOW | src/attr/__init__.py | 16 | CODE | |
| LOW | src/attr/__init__.py | 16 | CODE | |
| LOW | src/attr/__init__.py | 16 | CODE | |
| LOW | src/attr/__init__.py | 16 | CODE | |
| LOW | src/attr/__init__.py | 30 | CODE | |
| LOW | src/attr/__init__.py | 30 | CODE | |
| LOW | src/attr/__init__.py | 30 | CODE | |
| 5 more matches not shown… | ||||
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| HIGH | src/attr/_make.py | 590 | Create a new instance, based on the first positional argument with *changes* applied. .. tip:: On | STRING |
| HIGH | src/attr/_make.py | 1925 | Return the tuple of *attrs* attributes for a class or instance. The tuple also allows accessing the fields by | STRING |
| HIGH | src/attr/_make.py | 1976 | Return an ordered dictionary of *attrs* attributes for a class, whose keys are the attribute names. Args: | STRING |
| HIGH | src/attr/_next_gen.py | 656 | Inspect the class and return its effective build parameters. Warning: This feature is currently **expe | STRING |
| HIGH | src/attr/_funcs.py | 34 | Return the *attrs* attribute values of *inst* as a dict. Optionally recurse into other *attrs*-decorated class | STRING |
| HIGH | src/attr/_funcs.py | 237 | Return the *attrs* attribute values of *inst* as a tuple. Optionally recurse into other *attrs*-decorated clas | STRING |
| HIGH | src/attr/_funcs.py | 353 | Check whether *cls* is a class with *attrs* attributes. Args: cls (type): Class to introspect. Ra | STRING |
| HIGH | src/attr/_funcs.py | 381 | Copy *inst* and apply *changes*. This is different from `evolve` that applies the changes to the arguments | STRING |
| HIGH | src/attr/_funcs.py | 428 | Resolve any strings and forward annotations in type annotations. This is only required if you need concrete ty | STRING |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | tests/test_funcs.py | 104 | CODE | |
| LOW | tests/test_funcs.py | 343 | CODE | |
| LOW | tests/test_funcs.py | 111 | CODE | |
| LOW | tests/test_funcs.py | 352 | CODE | |
| LOW | src/attr/_make.py | 381 | CODE | |
| LOW | src/attr/_make.py | 1353 | CODE | |
| LOW | src/attr/_make.py | 2187 | CODE | |
| LOW | src/attr/_make.py | 676 | CODE | |
| LOW | src/attr/_make.py | 823 | CODE | |
| LOW | src/attr/_make.py | 859 | CODE | |
| LOW | src/attr/_make.py | 1478 | CODE | |
| LOW | src/attr/_funcs.py | 26 | CODE | |
| LOW | src/attr/_funcs.py | 152 | CODE | |
| LOW | src/attr/_funcs.py | 230 | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| HIGH | src/attr/validators.py | 669 | except self.exc_types: | CODE |
| HIGH | src/attr/_make.py | 996 | except ValueError: # noqa: PERF203 | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | tests/utils.py | 11 | CODE | |
| LOW | tests/utils.py | 40 | CODE | |
| LOW | src/attr/_make.py | 104 | CODE | |
| LOW | src/attr/_make.py | 1353 | CODE | |
| LOW | src/attr/_make.py | 2531 | CODE | |
| LOW | src/attr/_next_gen.py | 23 | CODE | |
| LOW | src/attr/_next_gen.py | 434 | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | src/attrs/__init__.py | 31 | __all__ = [ | CODE |
| LOW | src/attr/validators.py | 19 | __all__ = [ | CODE |
| LOW | src/attr/__init__.py | 45 | __all__ = [ | CODE |
| LOW | src/attr/converters.py | 11 | __all__ = [ | CODE |
| LOW | src/attr/_config.py | 3 | __all__ = ["get_run_validators", "set_run_validators"] | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | src/attr/_funcs.py | 494 | # Return the class so you can use it as a decorator too. | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | pyproject.toml | 273 | "DTZ", # datetime best practices don't matter in tests | STRING |
| MEDIUM | pyproject.toml | 285 | "TRY", # exception best practices don't matter in tests | STRING |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | typing_tests/mypy.py | 161 | # ConvCPipe(3.4) | COMMENT |
| LOW | src/attr/_compat.py | 81 | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | tests/test_setattr.py | 269 | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | src/attr/_make.py | 708 | # Check if the pre init method has more arguments than just `self` | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | src/attr/validators.py | 731 | except Exception: # noqa: BLE001, PERF203, S112 | STRING |