Skip to content

Commit fe8234e

Browse files
committed
refactor: replace getattr with direct attribute access
1 parent 3513c01 commit fe8234e

10 files changed

Lines changed: 54 additions & 50 deletions

File tree

interfacy/appearance/layout.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -165,20 +165,23 @@ class HelpLayout:
165165
)
166166

167167
layout_mode: Literal["auto", "adaptive", "template"] = "auto"
168+
subcommand_usage_token: str | None = None
168169

169170
# "bold": remove backticks in docstring and make text bold
170171
# "strip": remove backticks in docstring and leave plain text
171172
doc_inline_code_mode: Literal["bold", "strip"] = "bold"
173+
_default_field_width_base: int | None = field(default=None, init=False, repr=False)
174+
_pos_flag_width_base: int | None = field(default=None, init=False, repr=False)
172175

173176
def _get_default_field_width_base(self) -> int:
174-
base = getattr(self, "_default_field_width_base", None)
177+
base = self._default_field_width_base
175178
if base is None:
176179
base = self.default_field_width
177180
self._default_field_width_base = base
178181
return base
179182

180183
def _get_pos_flag_width_base(self) -> int:
181-
base = getattr(self, "_pos_flag_width_base", None)
184+
base = self._pos_flag_width_base
182185
if base is None:
183186
base = self.pos_flag_width
184187
self._pos_flag_width_base = base
@@ -194,7 +197,7 @@ def _compute_default_field_width_for_len(self, max_len: int) -> int:
194197
except (OSError, AttributeError):
195198
term_width = 80
196199

197-
ratio = max(1, getattr(self, "default_field_width_term_ratio", 5))
200+
ratio = max(1, self.default_field_width_term_ratio)
198201
term_cap = max(base_width, term_width // ratio)
199202
if self.default_field_width_max is not None:
200203
term_cap = min(term_cap, self.default_field_width_max)
@@ -212,9 +215,9 @@ def _compute_default_field_width_from_lengths(self, lengths: list[int]) -> int:
212215
except (OSError, AttributeError):
213216
term_width = 80
214217

215-
ratio = max(1, getattr(self, "default_field_width_term_ratio", 5))
218+
ratio = max(1, self.default_field_width_term_ratio)
216219
term_cap = max(base_width, term_width // ratio)
217-
soft_ratio = max(1, getattr(self, "default_field_width_soft_ratio", 8))
220+
soft_ratio = max(1, self.default_field_width_soft_ratio)
218221
soft_cap = max(base_width, term_width // soft_ratio)
219222
effective_cap = min(term_cap, soft_cap)
220223
if self.default_field_width_max is not None:
@@ -323,7 +326,7 @@ def get_parser_command_usage_suffix(self) -> str:
323326

324327
def get_subcommand_usage_token(self) -> str:
325328
"""Return the subcommand placeholder token used in usage lines."""
326-
legacy_placeholder = getattr(self, "subcommand_usage_token", None)
329+
legacy_placeholder = self.subcommand_usage_token
327330
if isinstance(legacy_placeholder, str):
328331
return legacy_placeholder
329332
return self.subcommand_usage_placeholder

interfacy/appearance/type_help.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,9 @@ def format(self, annotation: object) -> str:
7171

7272
def _resolve_type_token_styles(self) -> dict[str, TextStyle]:
7373
def pick(name: str) -> TextStyle:
74-
if self.theme is not None and hasattr(self.theme, name):
75-
return getattr(self.theme, name)
76-
return self.style
74+
if self.theme is None:
75+
return self.style
76+
return getattr(self.theme, name)
7777

7878
return {
7979
"name": self.style,

interfacy/argparse_backend/help_formatter.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ def start_section(self, heading: str | None) -> None:
5454
"""
5555
layout = self._get_help_layout()
5656
if layout is not None and heading not in (None, argparse.SUPPRESS):
57-
title_map = getattr(layout, "section_title_map", None)
57+
title_map = layout.section_title_map
5858
heading_text = str(heading).strip()
5959
heading_key = heading_text.rstrip(":").strip().lower()
6060
if isinstance(title_map, dict):
@@ -65,7 +65,7 @@ def start_section(self, heading: str | None) -> None:
6565
)
6666
if mapped:
6767
heading = mapped
68-
heading_style = getattr(layout, "section_heading_style", None)
68+
heading_style = layout.section_heading_style
6969
if heading_style is not None:
7070
heading = with_style(str(heading), heading_style)
7171
return super().start_section(heading)
@@ -92,7 +92,7 @@ def _primary_boolean_option_strings(action: argparse.Action) -> list[str]:
9292
if base_flag and not no_flag:
9393
no_flag = f"--no-{base_flag[2:]}"
9494

95-
default_val = getattr(action, "default", False)
95+
default_val = action.default
9696
primary_long = no_flag if bool(default_val) and no_flag else (base_flag or longs[0])
9797

9898
normalized: list[str] = []
@@ -249,10 +249,10 @@ def _format_usage(
249249

250250
layout = self._get_help_layout()
251251
if layout is not None:
252-
custom_prefix = getattr(layout, "usage_prefix", None)
252+
custom_prefix = layout.usage_prefix
253253
if custom_prefix is not None:
254254
prefix = custom_prefix
255-
usage_style = getattr(layout, "usage_style", None)
255+
usage_style = layout.usage_style
256256
if usage_style is not None:
257257
prefix = with_style(prefix, usage_style)
258258

@@ -335,15 +335,15 @@ def get_lines(
335335

336336
usage_text_style = None
337337
if layout is not None:
338-
custom_prefix = getattr(layout, "usage_prefix", None)
338+
custom_prefix = layout.usage_prefix
339339
if isinstance(usage, str) and custom_prefix is not None:
340340
usage = re.sub(
341341
r"^(?:\x1b\[[0-9;]*m)*\s*usage:\s*",
342342
"",
343343
usage,
344344
flags=re.IGNORECASE,
345345
)
346-
usage_text_style = getattr(layout, "usage_text_style", None)
346+
usage_text_style = layout.usage_text_style
347347
if usage_text_style is not None and isinstance(usage, str):
348348
usage = with_style(usage, usage_text_style)
349349

interfacy/click_backend/commands.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import click
77
from click.formatting import iter_rows, measure_table, term_len, wrap_text
88

9+
from interfacy.appearance.layout import HelpLayout
910
from interfacy.appearance.renderer import (
1011
SchemaHelpRenderer,
1112
command_has_grouped_subcommands,
@@ -17,15 +18,13 @@
1718
from interfacy.schema.schema import Argument, Command, ParserSchema
1819

1920

20-
def _uses_template_layout(layout: object) -> bool:
21-
layout_mode = getattr(layout, "layout_mode", None)
21+
def _uses_template_layout(layout: HelpLayout) -> bool:
22+
layout_mode = layout.layout_mode
2223
if layout_mode == "template":
2324
return True
2425
if layout_mode == "adaptive":
2526
return False
26-
return bool(
27-
getattr(layout, "format_option", None) or getattr(layout, "format_positional", None)
28-
)
27+
return bool(layout.format_option or layout.format_positional)
2928

3029

3130
class InterfacyClickHelpFormatter(click.HelpFormatter):

interfacy/click_backend/parser.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ def _get_value_from_state(
5656
option: _OptionLike,
5757
state: _ParsingStateLike,
5858
) -> object:
59-
if getattr(option, "nargs", 1) != -1:
59+
if option.nargs != -1:
6060
return super()._get_value_from_state(option_name, option, state)
6161

6262
collected: list[str] = []

interfacy/core.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ def __init__(
218218
self.help_position = help_position
219219
self._help_layout_explicit = help_layout is not None
220220
self._help_position_explicit = help_position is not None or (
221-
help_layout is not None and getattr(help_layout, "help_position", None) is not None
221+
help_layout is not None and help_layout.help_position is not None
222222
)
223223

224224
self.autorun = run
@@ -287,7 +287,8 @@ def _resolve_help_option_sort_from_layout(
287287
help_layout=help_layout,
288288
user_value=self.help_option_sort,
289289
user_validator=validate_help_option_sort,
290-
layout_default_attr="help_option_sort_default",
290+
layout_default_value=help_layout.help_option_sort_default,
291+
layout_default_name="help_option_sort_default",
291292
layout_resolver=resolve_help_option_sort_rules,
292293
default_rules=DEFAULT_HELP_OPTION_SORT_RULES,
293294
)
@@ -298,7 +299,8 @@ def _resolve_help_sort_from_layout(
298299
help_layout: HelpLayout,
299300
user_value: object,
300301
user_validator: Callable[[object], list[SortRuleT] | None],
301-
layout_default_attr: str,
302+
layout_default_value: object,
303+
layout_default_name: str,
302304
layout_resolver: Callable[..., list[SortRuleT] | None],
303305
default_rules: Sequence[SortRuleT],
304306
) -> list[SortRuleT]:
@@ -307,8 +309,8 @@ def _resolve_help_sort_from_layout(
307309
return list(user_rules)
308310

309311
layout_rules = layout_resolver(
310-
getattr(help_layout, layout_default_attr, None),
311-
value_name=f"{help_layout.__class__.__name__}.{layout_default_attr}",
312+
layout_default_value,
313+
value_name=f"{help_layout.__class__.__name__}.{layout_default_name}",
312314
)
313315
if layout_rules:
314316
return list(layout_rules)
@@ -332,7 +334,8 @@ def _resolve_help_subcommand_sort_from_layout(
332334
help_layout=help_layout,
333335
user_value=self.help_subcommand_sort,
334336
user_validator=validate_help_subcommand_sort,
335-
layout_default_attr="help_subcommand_sort_default",
337+
layout_default_value=help_layout.help_subcommand_sort_default,
338+
layout_default_name="help_subcommand_sort_default",
336339
layout_resolver=resolve_help_subcommand_sort_rules,
337340
default_rules=DEFAULT_HELP_SUBCOMMAND_SORT_RULES,
338341
)

interfacy/schema/builder.py

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -126,9 +126,8 @@ def _resolve_help_option_sort_value(
126126

127127
layout = self.parser.help_layout
128128
if layout is not None:
129-
layout_default = getattr(layout, "help_option_sort_default", None)
130129
layout_rules = resolve_help_option_sort_rules(
131-
layout_default,
130+
layout.help_option_sort_default,
132131
value_name=f"{layout.__class__.__name__}.help_option_sort_default",
133132
)
134133
if layout_rules:
@@ -148,9 +147,8 @@ def _resolve_help_subcommand_sort_value(
148147

149148
layout = self.parser.help_layout
150149
if layout is not None:
151-
layout_default = getattr(layout, "help_subcommand_sort_default", None)
152150
layout_rules = resolve_help_subcommand_sort_rules(
153-
layout_default,
151+
layout.help_subcommand_sort_default,
154152
value_name=f"{layout.__class__.__name__}.help_subcommand_sort_default",
155153
)
156154
if layout_rules:
@@ -160,19 +158,19 @@ def _resolve_help_subcommand_sort_value(
160158

161159
def _base_build_settings(self) -> _CommandBuildSettings:
162160
help_option_sort = self._resolve_help_option_sort_value(
163-
getattr(self.parser, "help_option_sort", None),
161+
self.parser.help_option_sort,
164162
value_name="help_option_sort",
165163
)
166164
help_subcommand_sort = self._resolve_help_subcommand_sort_value(
167-
getattr(self.parser, "help_subcommand_sort", None),
165+
self.parser.help_subcommand_sort,
168166
value_name="help_subcommand_sort",
169167
)
170168
return _CommandBuildSettings(
171-
include_inherited_methods=getattr(self.parser, "include_inherited_methods", False),
172-
include_classmethods=getattr(self.parser, "include_classmethods", False),
173-
expand_model_params=getattr(self.parser, "expand_model_params", True),
174-
model_expansion_max_depth=getattr(self.parser, "model_expansion_max_depth", 3),
175-
abbreviation_scope=getattr(self.parser, "abbreviation_scope", "top_level_options"),
169+
include_inherited_methods=self.parser.include_inherited_methods,
170+
include_classmethods=self.parser.include_classmethods,
171+
expand_model_params=self.parser.expand_model_params,
172+
model_expansion_max_depth=self.parser.model_expansion_max_depth,
173+
abbreviation_scope=self.parser.abbreviation_scope,
176174
help_option_sort=help_option_sort,
177175
help_subcommand_sort=help_subcommand_sort,
178176
)
@@ -858,7 +856,7 @@ def _argument_from_parameter(
858856
has_default=param.has_default,
859857
default=param.default if param.has_default else None,
860858
is_required=param.is_required,
861-
is_optional=getattr(param, "is_optional", False),
859+
is_optional=param.is_optional,
862860
kind=param.kind,
863861
description=param.description,
864862
)

interfacy/util.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -442,19 +442,18 @@ def get_annotation_choices(annotation: object, *, for_display: bool = False) ->
442442

443443
def get_param_choices(param: Parameter, *, for_display: bool = False) -> list[object] | None:
444444
"""Return choices for an objinspect Parameter, falling back to inferred Enum types."""
445-
choices = get_annotation_choices(getattr(param, "type", None), for_display=for_display)
445+
choices = get_annotation_choices(param.type, for_display=for_display)
446446
if choices:
447447
return choices
448448

449449
inferred = None
450-
if hasattr(param, "get_infered_type"):
451-
try:
452-
inferred = param.get_infered_type()
453-
except (AttributeError, KeyError, NameError, TypeError, ValueError):
454-
inferred = None
450+
try:
451+
inferred = param.get_infered_type()
452+
except (AttributeError, KeyError, NameError, TypeError, ValueError):
453+
inferred = None
455454

456-
if inferred is None and getattr(param, "has_default", False):
457-
default = getattr(param, "default", None)
455+
if inferred is None and param.has_default:
456+
default = param.default
458457
if isinstance(default, Enum):
459458
inferred = type(default)
460459

tests/common/test_type_help_tokenization.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99

1010
def _marker(text: str, style: TextStyle) -> str:
11-
color = getattr(style, "color", "none")
11+
color = style.color
1212
return f"<{color}>{text}</{color}>"
1313

1414

tests/schema/conftest.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ def make_command_stub(
7373
canonical_name=canonical,
7474
cli_name=canonical,
7575
aliases=tuple(aliases),
76-
raw_description=obj.description if hasattr(obj, "description") else None,
76+
raw_description=obj.description,
7777
help_layout=layout,
7878
)
7979

@@ -93,6 +93,8 @@ class FakeParser:
9393
reserved_flags: Sequence[str] = ("help",)
9494
command_key: str | None = "command"
9595
metadata: dict[str, Any] | None = None
96+
include_inherited_methods: bool = False
97+
include_classmethods: bool = False
9698
expand_model_params: bool = True
9799
model_expansion_max_depth: int = 3
98100
abbreviation_scope: str = "top_level_options"

0 commit comments

Comments
 (0)