diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index abc0485..897b99b 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -28,3 +28,16 @@ jobs: version: 0.5.6 args: check src: "." + deadcode: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: "3.11" + - name: Install deadcode + run: pip install deadcode + - name: Run deadcode + run: deadcode . diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 41febc3..076ee8a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,3 +16,7 @@ repos: - id: markdownlint args: - --fix + # - repo: https://github.com/albertas/deadcode + # rev: 2.4.1 + # hooks: + # - id: deadcode diff --git a/datamodel.py b/datamodel.py index f68a2ee..99141bf 100644 --- a/datamodel.py +++ b/datamodel.py @@ -57,11 +57,11 @@ def natural_sort_key(s): for text in re.split("([0-9]+)", s) ] - def GetColumnCount(self): + def GetColumnCount(self): # noqa: DC04 """Get number of columns.""" return len(self.columns) - def GetColumnType(self, col): + def GetColumnType(self, col): # noqa: DC04 """Get type of each column.""" columntypes = ( "string", @@ -77,7 +77,7 @@ def GetColumnType(self, col): ) return columntypes[col] - def GetChildren(self, parent, children): + def GetChildren(self, parent, children): # noqa: DC04 """Get child items of a parent.""" if not parent: for row in self.data: @@ -85,11 +85,11 @@ def GetChildren(self, parent, children): return len(self.data) return 0 - def IsContainer(self, item): + def IsContainer(self, item): # noqa: DC04 """Check if tem is a container.""" return not item - def GetParent(self, item): + def GetParent(self, item): # noqa: DC04 """Get parent item.""" return dv.NullDataViewItem @@ -117,7 +117,7 @@ def SetValue(self, value, item, col): row[col] = value return True - def Compare(self, item1, item2, column, ascending): + def Compare(self, item1, item2, column, ascending): # noqa: DC04 """Override to implement natural sorting.""" val1 = self.GetValue(item1, column) val2 = self.GetValue(item2, column) @@ -159,28 +159,6 @@ def AddEntry(self, data: list): self.data.append(data) self.ItemAdded(dv.NullDataViewItem, self.ObjectToItem(data)) - def UpdateEntry(self, data: list): - """Update an entry in the data model.""" - if (index := self.find_index(data[0])) is None: - return - item = self.data[index] - for i in range(0, len(data)): - if i in [self.columns["BOM_COL"], self.columns["POS_COL"]]: - item[i] = self.get_bom_pos_icon(data[i]) - elif i == self.columns["SIDE_COL"]: - item[i] = self.get_side_icon(data[i]) - else: - item[i] = data[i] - self.ItemChanged(self.ObjectToItem(item)) - - def RemoveEntry(self, ref: str): - """Remove an entry from the data model.""" - if (index := self.find_index(ref)) is None: - return - item = self.ObjectToItem(self.data[index]) - self.data.remove(self.data[index]) - self.ItemDeleted(dv.NullDataViewItem, item) - def RemoveAll(self): """Remove all entries from the data model.""" self.data.clear() diff --git a/helpers.py b/helpers.py index 2044df9..40ed1f3 100644 --- a/helpers.py +++ b/helpers.py @@ -8,11 +8,8 @@ PLUGIN_PATH = os.path.split(os.path.abspath(__file__))[0] -THT = 0 -SMD = 1 EXCLUDE_FROM_POS = 2 EXCLUDE_FROM_BOM = 3 -NOT_IN_SCHEMATIC = 4 def is_version8(version: str) -> bool: @@ -94,25 +91,6 @@ def loadIconScaled(filename, scale=1.0): return wx.Icon(bmp) -def GetListIcon(value, scale_factor): - """Get check or cross icon depending on passed value.""" - if value == 0: - return wx.dataview.DataViewIconText( - "", - loadIconScaled( - "mdi-check-color.png", - scale_factor, - ), - ) - return wx.dataview.DataViewIconText( - "", - loadIconScaled( - "mdi-close-color.png", - scale_factor, - ), - ) - - def natural_sort_collation(a, b): """Natural sort collation for use in sqlite.""" if a == b: @@ -173,55 +151,16 @@ def get_valid_footprints(board): return footprints -def get_footprint_keys(fp): - """Get keys from footprint for sorting.""" - try: - package = str(fp.GetFPID().GetLibItemName()) - except ValueError: - package = "" - try: - reference = int(re.search(r"\d+", fp.GetReference())[0]) - except ValueError: - reference = 0 - return (package, reference) - - def get_bit(value, bit): """Get the nth bit of a byte.""" return value & (1 << bit) -def set_bit(value, bit): - """Set the nth bit of a byte.""" - return value | (1 << bit) - - -def clear_bit(value, bit): - """Clear the nth bit of a byte.""" - return value & ~(1 << bit) - - def toggle_bit(value, bit): """Toggle the nth bit of a byte.""" return value ^ (1 << bit) -def get_tht(footprint): - """Get the THT property of a footprint.""" - if not footprint: - return None - val = footprint.GetAttributes() - return bool(get_bit(val, THT)) - - -def get_smd(footprint): - """Get the SMD property of a footprint.""" - if not footprint: - return None - val = footprint.GetAttributes() - return bool(get_bit(val, SMD)) - - def get_exclude_from_pos(footprint): """Get the 'exclude from POS' property of a footprint.""" if not footprint: @@ -238,93 +177,6 @@ def get_exclude_from_bom(footprint): return bool(get_bit(val, EXCLUDE_FROM_BOM)) -def get_not_in_schematic(footprint): - """Get the 'not in schematic' property of a footprint.""" - if not footprint: - return None - val = footprint.GetAttributes() - return bool(get_bit(val, NOT_IN_SCHEMATIC)) - - -def set_tht(footprint): - """Set the THT property of a footprint.""" - if not footprint: - return None - val = footprint.GetAttributes() - val = set_bit(val, THT) - footprint.SetAttributes(val) - return bool(get_bit(val, THT)) - - -def set_smd(footprint): - """Set the SMD property of a footprint.""" - if not footprint: - return None - val = footprint.GetAttributes() - val = set_bit(val, SMD) - footprint.SetAttributes(val) - return bool(get_bit(val, SMD)) - - -def set_exclude_from_pos(footprint, v): - """Set the 'exclude from POS' property of a footprint.""" - if not footprint: - return None - val = footprint.GetAttributes() - if v: - val = set_bit(val, EXCLUDE_FROM_POS) - else: - val = clear_bit(val, EXCLUDE_FROM_POS) - footprint.SetAttributes(val) - return bool(get_bit(val, EXCLUDE_FROM_POS)) - - -def set_exclude_from_bom(footprint, v): - """Set the 'exclude from BOM' property of a footprint.""" - if not footprint: - return None - val = footprint.GetAttributes() - if v: - val = set_bit(val, EXCLUDE_FROM_BOM) - else: - val = clear_bit(val, EXCLUDE_FROM_BOM) - footprint.SetAttributes(val) - return bool(get_bit(val, EXCLUDE_FROM_BOM)) - - -def set_not_in_schematic(footprint, v): - """Set the 'not in schematic' property of a footprint.""" - if not footprint: - return None - val = footprint.GetAttributes() - if v: - val = set_bit(val, NOT_IN_SCHEMATIC) - else: - val = clear_bit(val, NOT_IN_SCHEMATIC) - footprint.SetAttributes(val) - return bool(get_bit(val, NOT_IN_SCHEMATIC)) - - -def toggle_tht(footprint): - """Toggle the THT property of a footprint.""" - if not footprint: - return None - val = footprint.GetAttributes() - val = toggle_bit(val, THT) - footprint.SetAttributes(val) - return bool(get_bit(val, THT)) - - -def toggle_smd(footprint): - """Toggle the SMD property of a footprint.""" - if not footprint: - return None - val = footprint.GetAttributes() - val = toggle_bit(val, SMD) - footprint.SetAttributes(val) - return bool(get_bit(val, SMD)) - - def toggle_exclude_from_pos(footprint): """Toggle the 'exclude from POS' property of a footprint.""" if not footprint: @@ -343,13 +195,3 @@ def toggle_exclude_from_bom(footprint): val = toggle_bit(val, EXCLUDE_FROM_BOM) footprint.SetAttributes(val) return bool(get_bit(val, EXCLUDE_FROM_BOM)) - - -def toggle_not_in_schematic(footprint): - """Toggle the 'not in schematic' property of a footprint.""" - if not footprint: - return None - val = footprint.GetAttributes() - val = toggle_bit(val, NOT_IN_SCHEMATIC) - footprint.SetAttributes(val) - return bool(get_bit(val, NOT_IN_SCHEMATIC)) diff --git a/library.py b/library.py index c05085a..8254ce4 100644 --- a/library.py +++ b/library.py @@ -34,9 +34,6 @@ class LibraryState(Enum): class Library: """A storage class to get data from a sqlite database and write it back.""" - # no longer works - CSV_URL = "https://jlcpcb.com/componentSearch/uploadComponentInfo" - def __init__(self, parent): self.logger = logging.getLogger(__name__) self.parent = parent @@ -355,17 +352,6 @@ def get_all_mapping_data(self): ).fetchall() ] - def update_meta_data(self, filename, size, partcount, date, last_update): - """Update the meta data table.""" - with contextlib.closing(sqlite3.connect(self.partsdb_file)) as con, con as cur: - cur.execute("DELETE from meta") - cur.commit() - cur.execute( - "INSERT INTO meta VALUES (?, ?, ?, ?, ?)", - (filename, size, partcount, date, last_update), - ) - cur.commit() - def create_parts_table(self, columns): """Create the parts table.""" with contextlib.closing(sqlite3.connect(self.partsdb_file)) as con, con as cur: @@ -373,18 +359,10 @@ def create_parts_table(self, columns): cur.execute(f"CREATE TABLE IF NOT EXISTS parts ({cols})") cur.commit() - def insert_parts(self, data, cols): - """Insert many parts at once.""" - with contextlib.closing(sqlite3.connect(self.partsdb_file)) as con: - cols = ",".join(["?"] * cols) - query = f"INSERT INTO parts VALUES ({cols})" - con.executemany(query, data) - con.commit() - def get_part_details(self, number: str) -> dict: """Get the part details for a LCSC number using optimized FTS5 querying.""" with contextlib.closing(sqlite3.connect(self.partsdb_file)) as con: - con.row_factory = dict_factory + con.row_factory = dict_factory # noqa: DC05 cur = con.cursor() query = """SELECT "LCSC Part" AS lcsc, "Stock" AS stock, "Library Type" AS type FROM parts WHERE parts MATCH :number""" cur.execute(query, {"number": number}) diff --git a/mainwindow.py b/mainwindow.py index 34a62d9..076901e 100644 --- a/mainwindow.py +++ b/mainwindow.py @@ -96,7 +96,6 @@ def __init__(self, parent, kicad_provider=KicadProvider()): style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER | wx.MAXIMIZE_BOX, ) self.pcbnew = kicad_provider.get_pcbnew() - self.KicadBuildVersion = self.pcbnew.GetBuildVersion() self.window = wx.GetTopLevelParent(self) self.SetSize(HighResWxSize(self.window, wx.Size(1300, 800))) self.scale_factor = GetScaleFactor(self.window) @@ -105,8 +104,6 @@ def __init__(self, parent, kicad_provider=KicadProvider()): self.schematic_name = f"{self.board_name.split('.')[0]}.kicad_sch" self.hide_bom_parts = False self.hide_pos_parts = False - self.manufacturers = [] - self.packages = [] self.library: Library self.store: Store self.settings = {} @@ -674,16 +671,6 @@ def OnFootprintSelected(self, *_): # cause pcbnew to refresh the board with the changes to the selected footprint(s) self.pcbnew.Refresh() - def enable_all_buttons(self, state): - """Control state of all the buttons.""" - self.enable_top_buttons(state) - self.enable_part_specific_toolbar_buttons(state) - - def enable_top_buttons(self, state): - """Control the state of all the buttons in the top section.""" - for button in (ID_GENERATE, ID_DOWNLOAD, ID_LAYERS): - self.upper_toolbar.EnableTool(button, state) - def enable_part_specific_toolbar_buttons(self, state): """Control the state of all the buttons that relate to parts in toolbar on the right side.""" for button in ( @@ -754,20 +741,6 @@ def get_part_details(self, *_): if lcsc := self.partlist_data_model.get_lcsc(item): self.show_part_details_dialog(lcsc) - def get_column_by_name(self, column_title_to_find): - """Lookup a column in our main footprint table by matching its title.""" - for col in self.footprint_list.Columns: - if col.Title == column_title_to_find: - return col - return None - - def get_column_position_by_name(self, column_title_to_find): - """Lookup the index of a column in our main footprint table by matching its title.""" - col = self.get_column_by_name(column_title_to_find) - if not col: - return -1 - return self.footprint_list.GetColumnPosition(col) - def show_part_details_dialog(self, part): """Show the part details modal dialog.""" wx.BeginBusyCursor() @@ -816,9 +789,6 @@ def save_settings(self): ) as j: json.dump(self.settings, j) - def calculate_costs(self, *_): - """Hopefully we will be able to calculate the part costs in the future.""" - def select_part(self, *_): """Select a part from the library and assign it to the selected footprint(s).""" selection = {} @@ -1021,7 +991,7 @@ def __init__(self, event_destination): logging.StreamHandler.__init__(self) self.event_destination = event_destination - def emit(self, record): + def emit(self, record): # noqa: DC04 """Marshal the event over to the main thread.""" msg = self.format(record) wx.QueueEvent(self.event_destination, LogboxAppendEvent(msg=f"{msg}\n")) diff --git a/plugin.py b/plugin.py index 5072a4b..8e7ea04 100644 --- a/plugin.py +++ b/plugin.py @@ -10,7 +10,7 @@ class JLCPCBPlugin(ActionPlugin): """JLCPCBPlugin instance of ActionPlugin.""" - def defaults(self): + def defaults(self): # noqa: DC04 """Define defaults.""" # pylint: disable=attribute-defined-outside-init self.name = "JLCPCB Tools" @@ -18,12 +18,12 @@ def defaults(self): self.description = ( "Generate JLCPCB-compatible Gerber, Excellon, BOM and CPL files" ) - self.show_toolbar_button = True + self.show_toolbar_button = True # noqa: DC05 path, _ = os.path.split(os.path.abspath(__file__)) - self.icon_file_name = os.path.join(path, "jlcpcb-icon.png") - self._pcbnew_frame = None + self.icon_file_name = os.path.join(path, "jlcpcb-icon.png") # noqa: DC05 + self._pcbnew_frame = None # noqa: DC05 - def Run(self): + def Run(self): # noqa: DC04 """Overwrite Run.""" dialog = JLCPCBTools(None) dialog.Center() diff --git a/store.py b/store.py index 8f33831..f0a5236 100644 --- a/store.py +++ b/store.py @@ -86,7 +86,7 @@ def read_all(self) -> dict: """Read all parts from the database.""" with contextlib.closing(sqlite3.connect(self.dbfile)) as con, con as cur: con.create_collation("naturalsort", natural_sort_collation) - con.row_factory = dict_factory + con.row_factory = dict_factory # noqa: DC05 return cur.execute( f"SELECT * FROM part_info ORDER BY {self.order_by} COLLATE naturalsort {self.order_dir}" ).fetchall() @@ -94,7 +94,7 @@ def read_all(self) -> dict: def read_bom_parts(self) -> dict: """Read all parts that should be included in the BOM.""" with contextlib.closing(sqlite3.connect(self.dbfile)) as con, con as cur: - con.row_factory = dict_factory + con.row_factory = dict_factory # noqa: DC05 # Query all parts that are supposed to be in the BOM an have an lcsc number, group the references together subquery = "SELECT value, reference, footprint, lcsc FROM part_info WHERE exclude_from_bom = '0' AND lcsc != '' ORDER BY lcsc, reference" query = f"SELECT value, GROUP_CONCAT(reference) AS refs, footprint, lcsc FROM ({subquery}) GROUP BY lcsc" @@ -125,7 +125,7 @@ def update_part(self, part: dict): def get_part(self, ref: str) -> dict: """Get a part from the database by its reference.""" with contextlib.closing(sqlite3.connect(self.dbfile)) as con, con as cur: - con.row_factory = dict_factory + con.row_factory = dict_factory # noqa: DC05 return cur.execute( "SELECT * FROM part_info WHERE reference = :reference", {"reference": ref},