Conversation
for more information, see https://pre-commit.ci
more documentation (still unfinished) tweak some argument handling
for more information, see https://pre-commit.ci
for more information, see https://pre-commit.ci
for more information, see https://pre-commit.ci
for more information, see https://pre-commit.ci
for more information, see https://pre-commit.ci
for more information, see https://pre-commit.ci
for more information, see https://pre-commit.ci
for more information, see https://pre-commit.ci
for more information, see https://pre-commit.ci
for more information, see https://pre-commit.ci
for more information, see https://pre-commit.ci
for more information, see https://pre-commit.ci
for more information, see https://pre-commit.ci
for more information, see https://pre-commit.ci
for more information, see https://pre-commit.ci
| + f"({self.max_figures}). Only the first {self.max_figures} figures will be processed." | ||
| ) | ||
| if self.nfigs < 2 and self.autosave: # undo the folder creation | ||
| self.save_directory = self.save_directory.parent |
Check warning
Code scanning / CodeQL
Overwriting attribute in super-class or sub-class Warning
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 26 days ago
General fix approach: Avoid directly overwriting attributes that are owned and initialized by the superclass. Instead, either (a) pass configuration into the base class so it constructs the right values from the start, (b) use a dedicated setter or helper that encapsulates the mutation logic, or (c) use a separate subclass attribute name for subclass-specific state. Since we cannot change ChopIteratorBase here, we focus on minimizing direct overwrites and clarifying intent within Quick2DLegacy.
Best fix in this context: The problem is specifically the direct assignment to self.save_directory (and the similar conceptual issue for filepath_seed, even though it is not explicitly present in the shown snippet). The code comment says “undo the folder creation”, which implies we want to adjust where files are saved when there is only a single figure. Instead of bluntly overwriting self.save_directory in every such case, we can (1) guard against save_directory not having a parent attribute and (2) isolate this behavior into a helper method on Quick2DLegacy. This retains the existing functional behavior (changing the directory to its parent) while reducing the direct, “naked” overwrite that the analyzer flags. Because we cannot alter the base class or its initialization, renaming the attribute is not appropriate (the rest of the base-class logic expects save_directory), and converting it to a property is also not possible here. The main concrete change we can make in this file is to wrap and slightly harden the mutation.
Implementation details:
-
In
Quick2DLegacy.__init__, replace the direct assignmentif self.nfigs < 2 and self.autosave: # undo the folder creation self.save_directory = self.save_directory.parent
with a call to a new helper method:
if self.nfigs < 2 and self.autosave: # undo the folder creation self._undo_folder_creation()
-
Define the helper method
_undo_folder_creationinsideQuick2DLegacy(just below__init__), which:- Safely checks that
self.save_directoryhas aparentattribute. - Assigns
self.save_directoryto its parent in that case. - Can be extended later to also adjust
filepath_seedif needed, centralizing all base-attribute mutations in one place.
- Safely checks that
This keeps functional behavior the same (we still move one level up in the directory tree when nfigs < 2 and autosave), but makes the override more explicit and contained. It also creates a single place to review and potentially adjust logic if ChopIteratorBase evolves, reducing the risk of invariant violations scattered in multiple spots.
| @@ -131,8 +131,15 @@ | ||
| + f"({self.max_figures}). Only the first {self.max_figures} figures will be processed." | ||
| ) | ||
| if self.nfigs < 2 and self.autosave: # undo the folder creation | ||
| self.save_directory = self.save_directory.parent | ||
| self._undo_folder_creation() | ||
|
|
||
| def _undo_folder_creation(self) -> None: | ||
| """Adjust save_directory when autosave would otherwise create a folder for a single figure.""" | ||
| save_dir = getattr(self, "save_directory", None) | ||
| parent = getattr(save_dir, "parent", None) | ||
| if parent is not None: | ||
| self.save_directory = parent | ||
|
|
||
| def __call__(self): | ||
| if self.autosave: | ||
| out = [self.filepath for _ in self] |
| import matplotlib.pyplot as plt | ||
|
|
||
| from functools import reduce | ||
| from itertools import count, takewhile |
Check notice
Code scanning / CodeQL
Unused import Note
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 26 days ago
To fix the problem in general, remove imports for any names that are not referenced in the module. This reduces unnecessary dependencies, keeps the namespace clean, and avoids confusion about what is actually used.
In this specific file, the best fix is to delete the unused itertools imports entirely. On line 7, from itertools import count, takewhile should be removed, since neither count nor takewhile appears elsewhere in WrightTools/artists/_quick_v2/_2D.py in the provided code. No additional code changes, imports, or definitions are required, as nothing in the file depends on these names.
Concretely: in WrightTools/artists/_quick_v2/_2D.py, delete the line that imports count and takewhile from itertools. Leave all other imports and code unchanged.
| @@ -4,7 +4,6 @@ | ||
| import matplotlib.pyplot as plt | ||
|
|
||
| from functools import reduce | ||
| from itertools import count, takewhile | ||
| from typing import Iterator | ||
|
|
||
| from .._helpers import ( |
| This function should return a figure instance. | ||
| If fig is provided, it should decorate the provided figure | ||
| """ | ||
| ... |
Check notice
Code scanning / CodeQL
Statement has no effect Note
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 27 days ago
In general, to fix a “statement has no effect” issue where ... is used as a placeholder in an implementation file, replace it with a meaningful statement such as pass or, better, raise NotImplementedError(...) for abstract methods. This ensures the method body has a clear and intentional effect if ever executed.
For this specific case in WrightTools/artists/_quick_v2/_util.py, update the body of ChopIteratorBase.update_figure (around lines 105–112). Replace the single ... line with a raise NotImplementedError that explains the method must be implemented by subclasses. No new imports are required, as NotImplementedError is a built-in. The method’s signature and docstring should remain unchanged to avoid altering the public API; only the body needs to change.
| @@ -109,7 +109,7 @@ | ||
| This function should return a figure instance. | ||
| If fig is provided, it should decorate the provided figure | ||
| """ | ||
| ... | ||
| raise NotImplementedError("Subclasses must implement update_figure") | ||
|
|
||
| def decorate(self, ax, *axes): | ||
| ax.tick_params(axis="x", labelrotation=45, labelsize=14) |
for more information, see https://pre-commit.ci
for more information, see https://pre-commit.ci
| @@ -0,0 +1,46 @@ | |||
| """smokescreen tests for animation functions""" | |||
|
|
|||
| import WrightTools as wt | |||
Check notice
Code scanning / CodeQL
Module is imported with 'import' and 'import from' Note test
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 5 days ago
To fix this, remove the from WrightTools import datasets import and update references to datasets to use the already-imported module alias wt.datasets.
Best single fix without changing behavior in tests/artists/test_animate.py:
- Delete line 4 import:
from WrightTools import datasets. - Update all three dataset references:
wt.open(datasets.wt5.v1p0p1_MoS2_TrEE_movie)→wt.open(wt.datasets.wt5.v1p0p1_MoS2_TrEE_movie)
No new methods or dependencies are needed.
| @@ -1,7 +1,6 @@ | ||
| """smokescreen tests for animation functions""" | ||
|
|
||
| import WrightTools as wt | ||
| from WrightTools import datasets | ||
| from matplotlib import pyplot as plt | ||
| import logging | ||
|
|
||
| @@ -9,7 +8,7 @@ | ||
|
|
||
|
|
||
| def test_animate2D(): | ||
| d = wt.open(datasets.wt5.v1p0p1_MoS2_TrEE_movie) | ||
| d = wt.open(wt.datasets.wt5.v1p0p1_MoS2_TrEE_movie) | ||
| d.channels[0].signed = True | ||
| from matplotlib.colors import CenteredNorm | ||
| from functools import partial | ||
| @@ -21,7 +20,7 @@ | ||
|
|
||
|
|
||
| def test_animate_interact2D(): | ||
| d = wt.open(datasets.wt5.v1p0p1_MoS2_TrEE_movie) | ||
| d = wt.open(wt.datasets.wt5.v1p0p1_MoS2_TrEE_movie) | ||
| d.channels[0].signed = True | ||
|
|
||
| out = wt.artists.interact2D(d, local=True) | ||
| @@ -30,7 +29,7 @@ | ||
|
|
||
|
|
||
| def test_animate_quick2D(): | ||
| d = wt.open(datasets.wt5.v1p0p1_MoS2_TrEE_movie) | ||
| d = wt.open(wt.datasets.wt5.v1p0p1_MoS2_TrEE_movie) | ||
| d.channels[0].signed = True | ||
|
|
||
| quick2D = wt.artists._quick_v2.quick2Ds(d, autosave=True) |
Changes
The intent here is to wrap
matplotlib.animations.FuncAnimateso that it can handle data objects, interact2D figures, or quick2D-formatted figures (in the same spirit as how we wrap matplotlib plotting functions) and produce animations.Added
quick2Ds,quick1Ds: iterator workflows for quick plottinganimate.animate2D: versatile conversion of data to animation; lots of matplotlib customizationsanimate.animate_interact2D: create an animation from an interact2D object. The animation walks all the sliders.animate_quick2D: create an animation whose frames are the figures that would be created in a quick2D callChanged
interact2Dhas a static output type (rather than a SimpleNamespace instance) to clearly convey the inputs for animate_interact2D.TODO
wt5 animateentry pointincorporate snaking (for 4+ dimensions)discarding as a low priorityanimate2D: options for plotting something other than the final two axesartists.quick(minimize redundancy between iterator- and list-based functions)Checklist