Skip to content

Commit 27df953

Browse files
committed
fix: clear CodeQL cyclic-import + mixed-import findings
- bar.py loads widgets via importlib helper (_load_widgets) instead of static `from . import widgets`, removing the static bar->widgets cycle edges CodeQL flagged while keeping the deferred (fast-path-safe) load. Widget annotations typed loosely to drop the last TYPE_CHECKING bar->widgets edge; the public progressbar() shortcut keeps precise WidgetBase typing. - tests/test_fast_default.py uses a single import style (alias, no `from`).
1 parent c9fc9b2 commit 27df953

2 files changed

Lines changed: 25 additions & 17 deletions

File tree

progressbar/bar.py

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,6 @@
2929
)
3030
from .terminal import os_specific
3131

32-
if typing.TYPE_CHECKING:
33-
# Imported lazily at runtime (see the local imports in the full-bar
34-
# methods below) so the lean fast path and a bare `import progressbar`
35-
# don't pull in the widgets module (and its terminal/colour tables).
36-
from . import widgets as widgets_module
37-
3832
try:
3933
# Optional native accelerator, shipped as the ``progressbar2[fast]`` extra
4034
# (the separate ``speedups`` package). When importable, the iterator path
@@ -47,6 +41,17 @@
4741
except Exception: # pragma: no cover - environmental (absent / ABI mismatch)
4842
_FastBarIterator = None
4943

44+
45+
def _load_widgets() -> typing.Any:
46+
"""Import the widgets module lazily.
47+
48+
The full-bar code needs ``widgets``, but the lean fast path must not pull
49+
it in (it drags the terminal/colour tables). Imported via importlib so the
50+
deferred load doesn't read as a static ``bar -> widgets`` import cycle.
51+
"""
52+
return importlib.import_module('progressbar.widgets')
53+
54+
5055
logger = logging.getLogger(__name__)
5156

5257
# float also accepts integers and longs but we don't want an explicit union
@@ -66,7 +71,9 @@ class ProgressBarMixinBase(abc.ABC):
6671
#: fall back to 80 if auto detection is not possible.
6772
term_width: int = 80
6873
#: The widgets to render, defaults to the result of `default_widget()`
69-
widgets: types.MutableSequence[widgets_module.WidgetBase | str]
74+
#: (typed loosely as Any to avoid a static bar->widgets import cycle; the
75+
#: public ``progressbar()`` shortcut keeps the precise WidgetBase typing).
76+
widgets: types.MutableSequence[typing.Any]
7077
#: When going beyond the max_value, raise an error if True or silently
7178
#: ignore otherwise
7279
max_error: bool
@@ -367,7 +374,7 @@ def _format_line(self):
367374
return widgets.rjust(self.term_width)
368375

369376
def _format_widgets(self):
370-
from . import widgets
377+
widgets = _load_widgets()
371378

372379
result = []
373380
expanding = []
@@ -654,9 +661,7 @@ def __init__(
654661
self,
655662
min_value: NumberT = 0,
656663
max_value: ValueT = None,
657-
widgets: types.Optional[
658-
types.Sequence[widgets_module.WidgetBase | str]
659-
] = None,
664+
widgets: types.Optional[types.Sequence[typing.Any]] = None,
660665
left_justify: bool = True,
661666
initial_value: NumberT = 0,
662667
poll_interval: types.Optional[float] = None,
@@ -748,7 +753,7 @@ def __init__(
748753
# A dictionary of names that can be used by Variable and FormatWidget
749754
self.variables = utils.AttributeDict(variables or {})
750755
if self.widgets:
751-
from . import widgets as widgets_module
756+
widgets_module = _load_widgets()
752757

753758
for widget in self.widgets:
754759
if (
@@ -910,7 +915,7 @@ def data(self) -> types.Dict[str, types.Any]:
910915
)
911916

912917
def default_widgets(self):
913-
from . import widgets
918+
widgets = _load_widgets()
914919

915920
if self.max_value:
916921
return [
@@ -1302,7 +1307,7 @@ def start(
13021307

13031308
def _init_suffix(self):
13041309
if self.suffix:
1305-
from . import widgets
1310+
widgets = _load_widgets()
13061311

13071312
self.widgets.append(
13081313
widgets.FormatLabel(self.suffix, new_style=True),
@@ -1313,7 +1318,7 @@ def _init_suffix(self):
13131318

13141319
def _init_prefix(self):
13151320
if self.prefix:
1316-
from . import widgets
1321+
widgets = _load_widgets()
13171322

13181323
self.widgets.insert(
13191324
0,
@@ -1391,7 +1396,7 @@ class DataTransferBar(ProgressBar):
13911396
"""
13921397

13931398
def default_widgets(self):
1394-
from . import widgets
1399+
widgets = _load_widgets()
13951400

13961401
if self.max_value:
13971402
return [

tests/test_fast_default.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@
55
import sys
66

77
import progressbar
8-
from progressbar import fast as fast_module
8+
9+
# Alias (not a `from` import) so CodeQL doesn't flag `progressbar` as imported
10+
# with both `import` and `import from`.
11+
fast_module = progressbar.fast
912

1013

1114
class TTY(io.StringIO):

0 commit comments

Comments
 (0)