Compare commits
7 Commits
262ab202fc
...
e3d20c9bdc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e3d20c9bdc | ||
|
|
5add1f01c6 | ||
|
|
88e638a56e | ||
|
|
4ca5eb24c3 | ||
|
|
05ef2d766c | ||
|
|
db547cbdb7 | ||
|
|
005d17ee0a |
@ -83,7 +83,11 @@ class MusicMusterSignals(QObject):
|
||||
add_track_to_playlist_signal = pyqtSignal(int, int, int, str)
|
||||
enable_escape_signal = pyqtSignal(bool)
|
||||
next_track_changed_signal = pyqtSignal()
|
||||
search_songfacts_signal = pyqtSignal(str)
|
||||
search_wikipedia_signal = pyqtSignal(str)
|
||||
show_warning_signal = pyqtSignal(str, str)
|
||||
span_cells_signal = pyqtSignal(int, int, int, int)
|
||||
status_message_signal = pyqtSignal(str, int)
|
||||
|
||||
def __post_init__(self):
|
||||
super().__init__()
|
||||
|
||||
@ -1 +0,0 @@
|
||||
ui/icons_rc.py
|
||||
@ -8,6 +8,8 @@ from PyQt6.QtWebEngineWidgets import QWebEngineView
|
||||
from PyQt6.QtWidgets import QTabWidget
|
||||
from config import Config
|
||||
|
||||
from classes import MusicMusterSignals
|
||||
|
||||
|
||||
class InfoTabs(QTabWidget):
|
||||
"""
|
||||
@ -17,7 +19,9 @@ class InfoTabs(QTabWidget):
|
||||
def __init__(self, parent=None) -> None:
|
||||
super().__init__(parent)
|
||||
|
||||
# Dictionary to record when tabs were last updated (so we can
|
||||
self.signals = MusicMusterSignals()
|
||||
self.signals.search_songfacts_signal.connect(self.open_in_songfacts)
|
||||
self.signals.search_wikipedia_signal.connect(self.open_in_wikipedia)
|
||||
# re-use the oldest one later)
|
||||
self.last_update: Dict[QWebEngineView, datetime] = {}
|
||||
self.tabtitles: Dict[int, str] = {}
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
from os.path import basename
|
||||
from time import sleep
|
||||
from typing import (
|
||||
cast,
|
||||
@ -74,7 +73,7 @@ from ui.downloadcsv_ui import Ui_DateSelect # type: ignore
|
||||
from ui.main_window_ui import Ui_MainWindow # type: ignore
|
||||
from utilities import check_db, update_bitrates
|
||||
import helpers
|
||||
import icons_rc # noqa F401
|
||||
from ui import icons_rc # noqa F401
|
||||
import music
|
||||
|
||||
|
||||
@ -145,46 +144,6 @@ class CartButton(QPushButton):
|
||||
self.pgb.setGeometry(0, 0, self.width(), 10)
|
||||
|
||||
|
||||
class ImportTrack(QObject):
|
||||
import_error = pyqtSignal(str)
|
||||
importing = pyqtSignal(str)
|
||||
finished = pyqtSignal(PlaylistTab)
|
||||
|
||||
def __init__(self, playlist: PlaylistTab, filenames: list, row: int) -> None:
|
||||
super().__init__()
|
||||
self.filenames = filenames
|
||||
self.playlist = playlist
|
||||
self.row = row
|
||||
|
||||
def run(self):
|
||||
"""
|
||||
Create track objects from passed files and add to visible playlist
|
||||
"""
|
||||
|
||||
target_row = self.row
|
||||
with Session() as session:
|
||||
for fname in self.filenames:
|
||||
self.importing.emit(f"Importing {basename(fname)}")
|
||||
metadata = helpers.get_file_metadata(fname)
|
||||
try:
|
||||
track = Tracks(session, **metadata)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
return
|
||||
helpers.normalise_track(track.path)
|
||||
self.playlist.insert_track(session, track, target_row)
|
||||
# Insert next row under this one
|
||||
target_row += 1
|
||||
# We're importing potentially multiple tracks in a loop.
|
||||
# If there's an error adding the track to the Tracks
|
||||
# table, the session will rollback, thus losing any
|
||||
# previous additions in this loop. So, commit now to
|
||||
# lock in what we've just done.
|
||||
session.commit()
|
||||
self.playlist.save_playlist(session)
|
||||
self.finished.emit(self.playlist)
|
||||
|
||||
|
||||
class Window(QMainWindow, Ui_MainWindow):
|
||||
def __init__(self, parent=None, *args, **kwargs) -> None:
|
||||
super().__init__(parent)
|
||||
@ -476,7 +435,7 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
closing_tab_playlist_id = self.tabPlaylist.widget(tab_index).playlist_id
|
||||
if current_track_playlist_id:
|
||||
if closing_tab_playlist_id == current_track_playlist_id:
|
||||
self.statusbar.showMessage("Can't close current track playlist", 5000)
|
||||
self.show_status_message("Can't close current track playlist", 5000)
|
||||
return False
|
||||
|
||||
# Record playlist as closed and update remaining playlist tabs
|
||||
@ -523,10 +482,10 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
self.actionResume.triggered.connect(self.resume)
|
||||
self.actionSave_as_template.triggered.connect(self.save_as_template)
|
||||
self.actionSearch_title_in_Songfacts.triggered.connect(
|
||||
lambda: self.tabPlaylist.currentWidget().lookup_row_in_songfacts()
|
||||
self.lookup_row_in_songfacts
|
||||
)
|
||||
self.actionSearch_title_in_Wikipedia.triggered.connect(
|
||||
lambda: self.tabPlaylist.currentWidget().lookup_row_in_wikipedia()
|
||||
self.lookup_row_in_wikipedia
|
||||
)
|
||||
self.actionSearch.triggered.connect(self.search_playlist)
|
||||
self.actionSelect_duplicate_rows.triggered.connect(
|
||||
@ -551,6 +510,8 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
|
||||
self.signals.enable_escape_signal.connect(self.enable_escape)
|
||||
self.signals.next_track_changed_signal.connect(self.update_headers)
|
||||
self.signals.status_message_signal.connect(self.show_status_message)
|
||||
self.signals.show_warning_signal.connect(self.show_warning)
|
||||
|
||||
self.timer10.timeout.connect(self.tick_10ms)
|
||||
self.timer500.timeout.connect(self.tick_500ms)
|
||||
@ -638,7 +599,7 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
"""
|
||||
|
||||
self.actionPlay_next.setEnabled(False)
|
||||
self.statusbar.showMessage("Play controls: Disabled", 0)
|
||||
self.show_status_message("Play controls: Disabled", 0)
|
||||
|
||||
def download_played_tracks(self) -> None:
|
||||
"""Download a CSV of played tracks"""
|
||||
@ -689,7 +650,7 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
"""
|
||||
|
||||
self.actionPlay_next.setEnabled(True)
|
||||
self.statusbar.showMessage("Play controls: Enabled", 0)
|
||||
self.show_status_message("Play controls: Enabled", 0)
|
||||
|
||||
def export_playlist_tab(self) -> None:
|
||||
"""Export the current playlist to an m3u file"""
|
||||
@ -786,7 +747,21 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
txt = ""
|
||||
tags = helpers.get_tags(fname)
|
||||
title = tags["title"]
|
||||
if not title:
|
||||
helpers.show_warning(
|
||||
self,
|
||||
"Problem with track file",
|
||||
f"{fname} does not have a title tag",
|
||||
)
|
||||
continue
|
||||
artist = tags["artist"]
|
||||
if not artist:
|
||||
helpers.show_warning(
|
||||
self,
|
||||
"Problem with track file",
|
||||
f"{fname} does not have an artist tag",
|
||||
)
|
||||
continue
|
||||
count = 0
|
||||
possible_matches = Tracks.search_titles(session, title)
|
||||
if possible_matches:
|
||||
@ -813,33 +788,10 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
continue
|
||||
new_tracks.append(fname)
|
||||
|
||||
# Import in separate thread
|
||||
self.import_thread = QThread()
|
||||
self.worker = ImportTrack(
|
||||
self.active_tab(),
|
||||
new_tracks,
|
||||
self.active_tab().get_new_row_number(),
|
||||
# Pass to model to manage the import
|
||||
self.active_model().import_tracks(
|
||||
new_tracks, self.active_tab().get_selected_row_number()
|
||||
)
|
||||
self.worker.moveToThread(self.import_thread)
|
||||
self.import_thread.started.connect(self.worker.run)
|
||||
self.worker.finished.connect(self.import_thread.quit)
|
||||
self.worker.finished.connect(self.worker.deleteLater)
|
||||
self.import_thread.finished.connect(self.import_thread.deleteLater)
|
||||
self.worker.import_error.connect(
|
||||
lambda msg: helpers.show_warning(
|
||||
self, "Import error", "Error importing " + msg
|
||||
)
|
||||
)
|
||||
self.worker.importing.connect(lambda msg: self.statusbar.showMessage(msg, 5000))
|
||||
self.worker.finished.connect(self.import_complete)
|
||||
self.import_thread.start()
|
||||
|
||||
def import_complete(self):
|
||||
"""
|
||||
Called by thread when track import complete
|
||||
"""
|
||||
|
||||
self.statusbar.showMessage("Imports complete")
|
||||
|
||||
def insert_header(self) -> None:
|
||||
"""Show dialog box to enter header text and add to playlist"""
|
||||
@ -887,6 +839,36 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
if record.f_int and record.f_int >= 0:
|
||||
self.tabPlaylist.setCurrentIndex(record.f_int)
|
||||
|
||||
def lookup_row_in_songfacts(self) -> None:
|
||||
"""
|
||||
Display songfacts page for title in highlighted row
|
||||
"""
|
||||
|
||||
row_number = self.active_tab().get_selected_row_number()
|
||||
if row_number is None:
|
||||
return
|
||||
|
||||
track_info = self.active_model().get_row_info(row_number)
|
||||
if track_info is None:
|
||||
return
|
||||
|
||||
self.signals.search_songfacts_signal.emit(track_info.title)
|
||||
|
||||
def lookup_row_in_wikipedia(self) -> None:
|
||||
"""
|
||||
Display Wikipedia page for title in highlighted row
|
||||
"""
|
||||
|
||||
row_number = self.active_tab().get_selected_row_number()
|
||||
if row_number is None:
|
||||
return
|
||||
|
||||
track_info = self.active_model().get_row_info(row_number)
|
||||
if track_info is None:
|
||||
return
|
||||
|
||||
self.signals.search_wikipedia_signal.emit(track_info.title)
|
||||
|
||||
def move_playlist_rows(
|
||||
self, session: scoped_session, playlistrows: Sequence[PlaylistRows]
|
||||
) -> None:
|
||||
@ -1206,7 +1188,7 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
playlist_id = self.active_tab().playlist_id
|
||||
playlist = session.get(Playlists, playlist_id)
|
||||
if playlist:
|
||||
new_name = self.solicit_playlist_name(playlist.name)
|
||||
new_name = self.solicit_playlist_name(session, playlist.name)
|
||||
if new_name:
|
||||
playlist.rename(session, new_name)
|
||||
idx = self.tabBar.currentIndex()
|
||||
@ -1355,6 +1337,14 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
# self.tabPlaylist.setCurrentWidget(self.current_track.playlist_tab)
|
||||
# self.tabPlaylist.currentWidget().scroll_current_to_top()
|
||||
|
||||
def show_warning(self, title: str, body: str) -> None:
|
||||
"""
|
||||
Display a warning dialog
|
||||
"""
|
||||
|
||||
print(f"show_warning({title=}, {body=})")
|
||||
QMessageBox.warning(self, title, body)
|
||||
|
||||
def show_next(self) -> None:
|
||||
"""Scroll to show next track"""
|
||||
|
||||
@ -1364,6 +1354,13 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
# self.tabPlaylist.setCurrentWidget(self.next_track.playlist_tab)
|
||||
# self.tabPlaylist.currentWidget().scroll_next_to_top()
|
||||
|
||||
def show_status_message(self, message: str, timing: int) -> None:
|
||||
"""
|
||||
Show status message in status bar for timing milliseconds
|
||||
"""
|
||||
|
||||
self.statusbar.showMessage(message, timing)
|
||||
|
||||
def solicit_playlist_name(
|
||||
self, session: scoped_session, default: str = ""
|
||||
) -> Optional[str]:
|
||||
|
||||
@ -2,12 +2,16 @@ from dataclasses import dataclass
|
||||
from datetime import datetime, timedelta
|
||||
from enum import auto, Enum
|
||||
from operator import attrgetter
|
||||
from os.path import basename
|
||||
from typing import List, Optional
|
||||
|
||||
from PyQt6.QtCore import (
|
||||
pyqtSignal,
|
||||
QAbstractTableModel,
|
||||
QModelIndex,
|
||||
QObject,
|
||||
Qt,
|
||||
QThread,
|
||||
QVariant,
|
||||
)
|
||||
from PyQt6.QtGui import (
|
||||
@ -22,8 +26,10 @@ from dbconfig import scoped_session, Session
|
||||
from helpers import (
|
||||
file_is_unreadable,
|
||||
get_embedded_time,
|
||||
get_file_metadata,
|
||||
get_relative_date,
|
||||
open_in_audacity,
|
||||
normalise_track,
|
||||
ms_to_mmss,
|
||||
set_track_metadata,
|
||||
)
|
||||
@ -150,7 +156,9 @@ class PlaylistModel(QAbstractTableModel):
|
||||
if playlist_id != self.playlist_id:
|
||||
return
|
||||
|
||||
self.insert_row(proposed_row_number=new_row_number, track_id=track_id, note=note)
|
||||
self.insert_row(
|
||||
proposed_row_number=new_row_number, track_id=track_id, note=note
|
||||
)
|
||||
|
||||
def add_track_to_header(
|
||||
self,
|
||||
@ -634,6 +642,28 @@ class PlaylistModel(QAbstractTableModel):
|
||||
|
||||
return prd.note
|
||||
|
||||
def import_tracks(
|
||||
self, new_tracks: List[str], proposed_row_number: Optional[int]
|
||||
) -> None:
|
||||
"""
|
||||
Import the file paths listed in new_tracks. The files have already been sanitised
|
||||
so no further checks needed. Import in a separate thread as this is slow.
|
||||
"""
|
||||
|
||||
# Import in separate thread
|
||||
self.import_thread = QThread()
|
||||
self.worker = ImportTrack(
|
||||
self,
|
||||
new_tracks,
|
||||
proposed_row_number,
|
||||
)
|
||||
self.worker.moveToThread(self.import_thread)
|
||||
self.import_thread.started.connect(self.worker.run)
|
||||
self.worker.import_finished.connect(self.import_thread.quit)
|
||||
self.worker.import_finished.connect(self.worker.deleteLater)
|
||||
self.import_thread.finished.connect(self.import_thread.deleteLater)
|
||||
self.import_thread.start()
|
||||
|
||||
def is_header_row(self, row_number: int) -> bool:
|
||||
"""
|
||||
Return True if row is a header row, else False
|
||||
@ -824,11 +854,16 @@ class PlaylistModel(QAbstractTableModel):
|
||||
|
||||
def remove_track(self, row_number: int) -> None:
|
||||
"""
|
||||
Remove track from row
|
||||
Remove track from row, retaining row as a header row
|
||||
"""
|
||||
|
||||
# TODO
|
||||
print(f"remove_track({row_number=})")
|
||||
with Session() as session:
|
||||
plr = session.get(PlaylistRows, self.playlist_rows[row_number].plrid)
|
||||
if plr:
|
||||
plr.track_id = None
|
||||
self.refresh_row(session, row_number)
|
||||
self.invalidate_row(row_number)
|
||||
|
||||
|
||||
def rescan_track(self, row_number: int) -> None:
|
||||
"""
|
||||
@ -882,6 +917,7 @@ class PlaylistModel(QAbstractTableModel):
|
||||
return
|
||||
track_sequence.next = PlaylistTrack()
|
||||
self.invalidate_row(next_row_was)
|
||||
self.signals.next_track_changed_signal.emit()
|
||||
return
|
||||
|
||||
# Update playing_trtack
|
||||
@ -905,6 +941,7 @@ class PlaylistModel(QAbstractTableModel):
|
||||
return
|
||||
track_sequence.next.set_plr(session, plr)
|
||||
self.signals.next_track_changed_signal.emit()
|
||||
self.signals.search_wikipedia_signal.emit(self.playlist_rows[row_number].title)
|
||||
self.invalidate_row(row_number)
|
||||
self.update_track_times()
|
||||
|
||||
@ -1079,3 +1116,46 @@ class PlaylistModel(QAbstractTableModel):
|
||||
self.index(updated_row, Col.START_TIME.value),
|
||||
self.index(updated_row, Col.END_TIME.value),
|
||||
)
|
||||
|
||||
|
||||
class ImportTrack(QObject):
|
||||
import_finished = pyqtSignal()
|
||||
|
||||
def __init__(self, model: PlaylistModel, filenames: List[str], row_number) -> None:
|
||||
super().__init__()
|
||||
self.model = model
|
||||
self.filenames = filenames
|
||||
self.row_number = row_number
|
||||
self.signals = MusicMusterSignals()
|
||||
|
||||
def run(self):
|
||||
"""
|
||||
Create track objects from passed files and add to visible playlist
|
||||
"""
|
||||
|
||||
target_row = self.row_number
|
||||
with Session() as session:
|
||||
for fname in self.filenames:
|
||||
self.signals.status_message_signal.emit(
|
||||
f"Importing {basename(fname)}", 5000
|
||||
)
|
||||
metadata = get_file_metadata(fname)
|
||||
try:
|
||||
track = Tracks(session, **metadata)
|
||||
except Exception as e:
|
||||
self.signals.show_warning_signal.emit("Error importing track", e)
|
||||
return
|
||||
normalise_track(track.path)
|
||||
self.model.insert_row(self.row_number, track.id)
|
||||
# Insert next row under this one
|
||||
target_row += 1
|
||||
# We're importing potentially multiple tracks in a loop.
|
||||
# If there's an error adding the track to the Tracks
|
||||
# table, the session will rollback, thus losing any
|
||||
# previous additions in this loop. So, commit now to
|
||||
# lock in what we've just done.
|
||||
session.commit()
|
||||
self.signals.status_message_signal.emit(
|
||||
f"{len(self.filenames)} tracks imported", 10000
|
||||
)
|
||||
self.import_finished.emit()
|
||||
|
||||
@ -472,7 +472,7 @@ class PlaylistTab(QTableView):
|
||||
Set selected row as next track
|
||||
"""
|
||||
|
||||
selected_row = self._get_selected_row()
|
||||
selected_row = self.get_selected_row_number()
|
||||
if selected_row is None:
|
||||
return
|
||||
model = cast(PlaylistModel, self.model())
|
||||
@ -541,13 +541,13 @@ class PlaylistTab(QTableView):
|
||||
# Mark unplayed
|
||||
if track_row and model.is_unplayed_row(row_number):
|
||||
self._add_context_menu(
|
||||
"Mark unplayed", lambda: model.mark_unplayed(self._get_selected_rows())
|
||||
"Mark unplayed", lambda: self._mark_as_unplayed(self._get_selected_rows())
|
||||
)
|
||||
|
||||
# Unmark as next
|
||||
if next_row:
|
||||
self._add_context_menu(
|
||||
"Unmark as next track", lambda: model.set_next_row(None)
|
||||
"Unmark as next track", lambda: self._unmark_as_next()
|
||||
)
|
||||
|
||||
# ----------------------
|
||||
@ -582,7 +582,7 @@ class PlaylistTab(QTableView):
|
||||
|
||||
# Track path TODO
|
||||
if track_row:
|
||||
self._add_context_menu("Copy track path", lambda: print("Track path"))
|
||||
self._add_context_menu("Copy track path", lambda: self._copy_path(row_number))
|
||||
|
||||
def _calculate_end_time(
|
||||
self, start: Optional[datetime], duration: int
|
||||
@ -624,7 +624,8 @@ class PlaylistTab(QTableView):
|
||||
to the clipboard. Otherwise, return None.
|
||||
"""
|
||||
|
||||
track_path = self._get_row_track_path(row_number)
|
||||
model = cast(PlaylistModel, self.model())
|
||||
track_path = model.get_row_info(row_number).path
|
||||
if not track_path:
|
||||
return
|
||||
|
||||
@ -638,8 +639,9 @@ class PlaylistTab(QTableView):
|
||||
track_path = track_path.replace(old, new)
|
||||
|
||||
cb = QApplication.clipboard()
|
||||
cb.clear(mode=cb.Mode.Clipboard)
|
||||
cb.setText(track_path, mode=cb.Mode.Clipboard)
|
||||
if cb:
|
||||
cb.clear(mode=cb.Mode.Clipboard)
|
||||
cb.setText(track_path, mode=cb.Mode.Clipboard)
|
||||
|
||||
def _delete_rows(self) -> None:
|
||||
"""
|
||||
@ -663,18 +665,6 @@ class PlaylistTab(QTableView):
|
||||
model = cast(PlaylistModel, self.model())
|
||||
model.delete_rows(self._get_selected_rows())
|
||||
|
||||
def _get_selected_row(self) -> Optional[int]:
|
||||
"""
|
||||
Return row_number number of first selected row,
|
||||
or None if none selected
|
||||
"""
|
||||
|
||||
sm = self.selectionModel()
|
||||
if sm:
|
||||
if sm.hasSelection():
|
||||
return sm.selectedIndexes()[0].row()
|
||||
return None
|
||||
|
||||
def _get_selected_rows(self) -> List[int]:
|
||||
"""Return a list of selected row numbers sorted by row"""
|
||||
|
||||
@ -739,6 +729,13 @@ class PlaylistTab(QTableView):
|
||||
# else:
|
||||
# return
|
||||
|
||||
def _mark_as_unplayed(self, row_numbers: List[int]) -> None:
|
||||
"""Rescan track"""
|
||||
|
||||
model = cast(PlaylistModel, self.model())
|
||||
model.mark_unplayed(row_numbers)
|
||||
self.clear_selection()
|
||||
|
||||
def _obs_change_scene(self, current_row: int) -> None:
|
||||
"""
|
||||
Try to change OBS scene to the name passed
|
||||
@ -1017,3 +1014,10 @@ class PlaylistTab(QTableView):
|
||||
return
|
||||
|
||||
self.setSpan(row, column, rowSpan, columnSpan)
|
||||
|
||||
def _unmark_as_next(self) -> None:
|
||||
"""Rescan track"""
|
||||
|
||||
model = cast(PlaylistModel, self.model())
|
||||
model.set_next_row(None)
|
||||
self.clear_selection()
|
||||
|
||||
@ -75,7 +75,13 @@ def main():
|
||||
continue
|
||||
new_tags = get_tags(new_path)
|
||||
new_title = new_tags["title"]
|
||||
if not new_title:
|
||||
print(f"{new_fname} does not have a title tag")
|
||||
sys.exit(1)
|
||||
new_artist = new_tags["artist"]
|
||||
if not new_artist:
|
||||
print(f"{new_fname} does not have an artist tag")
|
||||
sys.exit(1)
|
||||
bitrate = new_tags["bitrate"]
|
||||
|
||||
# If same filename exists in parent direcory, check tags
|
||||
|
||||
59
poetry.lock
generated
59
poetry.lock
generated
@ -859,39 +859,39 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "mypy"
|
||||
version = "1.6.1"
|
||||
version = "1.7.0"
|
||||
description = "Optional static typing for Python"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "mypy-1.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e5012e5cc2ac628177eaac0e83d622b2dd499e28253d4107a08ecc59ede3fc2c"},
|
||||
{file = "mypy-1.6.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d8fbb68711905f8912e5af474ca8b78d077447d8f3918997fecbf26943ff3cbb"},
|
||||
{file = "mypy-1.6.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21a1ad938fee7d2d96ca666c77b7c494c3c5bd88dff792220e1afbebb2925b5e"},
|
||||
{file = "mypy-1.6.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b96ae2c1279d1065413965c607712006205a9ac541895004a1e0d4f281f2ff9f"},
|
||||
{file = "mypy-1.6.1-cp310-cp310-win_amd64.whl", hash = "sha256:40b1844d2e8b232ed92e50a4bd11c48d2daa351f9deee6c194b83bf03e418b0c"},
|
||||
{file = "mypy-1.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:81af8adaa5e3099469e7623436881eff6b3b06db5ef75e6f5b6d4871263547e5"},
|
||||
{file = "mypy-1.6.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8c223fa57cb154c7eab5156856c231c3f5eace1e0bed9b32a24696b7ba3c3245"},
|
||||
{file = "mypy-1.6.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8032e00ce71c3ceb93eeba63963b864bf635a18f6c0c12da6c13c450eedb183"},
|
||||
{file = "mypy-1.6.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4c46b51de523817a0045b150ed11b56f9fff55f12b9edd0f3ed35b15a2809de0"},
|
||||
{file = "mypy-1.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:19f905bcfd9e167159b3d63ecd8cb5e696151c3e59a1742e79bc3bcb540c42c7"},
|
||||
{file = "mypy-1.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:82e469518d3e9a321912955cc702d418773a2fd1e91c651280a1bda10622f02f"},
|
||||
{file = "mypy-1.6.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d4473c22cc296425bbbce7e9429588e76e05bc7342da359d6520b6427bf76660"},
|
||||
{file = "mypy-1.6.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59a0d7d24dfb26729e0a068639a6ce3500e31d6655df8557156c51c1cb874ce7"},
|
||||
{file = "mypy-1.6.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:cfd13d47b29ed3bbaafaff7d8b21e90d827631afda134836962011acb5904b71"},
|
||||
{file = "mypy-1.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:eb4f18589d196a4cbe5290b435d135dee96567e07c2b2d43b5c4621b6501531a"},
|
||||
{file = "mypy-1.6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:41697773aa0bf53ff917aa077e2cde7aa50254f28750f9b88884acea38a16169"},
|
||||
{file = "mypy-1.6.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7274b0c57737bd3476d2229c6389b2ec9eefeb090bbaf77777e9d6b1b5a9d143"},
|
||||
{file = "mypy-1.6.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbaf4662e498c8c2e352da5f5bca5ab29d378895fa2d980630656178bd607c46"},
|
||||
{file = "mypy-1.6.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bb8ccb4724f7d8601938571bf3f24da0da791fe2db7be3d9e79849cb64e0ae85"},
|
||||
{file = "mypy-1.6.1-cp38-cp38-win_amd64.whl", hash = "sha256:68351911e85145f582b5aa6cd9ad666c8958bcae897a1bfda8f4940472463c45"},
|
||||
{file = "mypy-1.6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:49ae115da099dcc0922a7a895c1eec82c1518109ea5c162ed50e3b3594c71208"},
|
||||
{file = "mypy-1.6.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8b27958f8c76bed8edaa63da0739d76e4e9ad4ed325c814f9b3851425582a3cd"},
|
||||
{file = "mypy-1.6.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:925cd6a3b7b55dfba252b7c4561892311c5358c6b5a601847015a1ad4eb7d332"},
|
||||
{file = "mypy-1.6.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8f57e6b6927a49550da3d122f0cb983d400f843a8a82e65b3b380d3d7259468f"},
|
||||
{file = "mypy-1.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:a43ef1c8ddfdb9575691720b6352761f3f53d85f1b57d7745701041053deff30"},
|
||||
{file = "mypy-1.6.1-py3-none-any.whl", hash = "sha256:4cbe68ef919c28ea561165206a2dcb68591c50f3bcf777932323bc208d949cf1"},
|
||||
{file = "mypy-1.6.1.tar.gz", hash = "sha256:4d01c00d09a0be62a4ca3f933e315455bde83f37f892ba4b08ce92f3cf44bcc1"},
|
||||
{file = "mypy-1.7.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5da84d7bf257fd8f66b4f759a904fd2c5a765f70d8b52dde62b521972a0a2357"},
|
||||
{file = "mypy-1.7.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a3637c03f4025f6405737570d6cbfa4f1400eb3c649317634d273687a09ffc2f"},
|
||||
{file = "mypy-1.7.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b633f188fc5ae1b6edca39dae566974d7ef4e9aaaae00bc36efe1f855e5173ac"},
|
||||
{file = "mypy-1.7.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d6ed9a3997b90c6f891138e3f83fb8f475c74db4ccaa942a1c7bf99e83a989a1"},
|
||||
{file = "mypy-1.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:1fe46e96ae319df21359c8db77e1aecac8e5949da4773c0274c0ef3d8d1268a9"},
|
||||
{file = "mypy-1.7.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:df67fbeb666ee8828f675fee724cc2cbd2e4828cc3df56703e02fe6a421b7401"},
|
||||
{file = "mypy-1.7.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a79cdc12a02eb526d808a32a934c6fe6df07b05f3573d210e41808020aed8b5d"},
|
||||
{file = "mypy-1.7.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f65f385a6f43211effe8c682e8ec3f55d79391f70a201575def73d08db68ead1"},
|
||||
{file = "mypy-1.7.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0e81ffd120ee24959b449b647c4b2fbfcf8acf3465e082b8d58fd6c4c2b27e46"},
|
||||
{file = "mypy-1.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:f29386804c3577c83d76520abf18cfcd7d68264c7e431c5907d250ab502658ee"},
|
||||
{file = "mypy-1.7.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:87c076c174e2c7ef8ab416c4e252d94c08cd4980a10967754f91571070bf5fbe"},
|
||||
{file = "mypy-1.7.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6cb8d5f6d0fcd9e708bb190b224089e45902cacef6f6915481806b0c77f7786d"},
|
||||
{file = "mypy-1.7.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d93e76c2256aa50d9c82a88e2f569232e9862c9982095f6d54e13509f01222fc"},
|
||||
{file = "mypy-1.7.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:cddee95dea7990e2215576fae95f6b78a8c12f4c089d7e4367564704e99118d3"},
|
||||
{file = "mypy-1.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:d01921dbd691c4061a3e2ecdbfbfad029410c5c2b1ee88946bf45c62c6c91210"},
|
||||
{file = "mypy-1.7.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:185cff9b9a7fec1f9f7d8352dff8a4c713b2e3eea9c6c4b5ff7f0edf46b91e41"},
|
||||
{file = "mypy-1.7.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7a7b1e399c47b18feb6f8ad4a3eef3813e28c1e871ea7d4ea5d444b2ac03c418"},
|
||||
{file = "mypy-1.7.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc9fe455ad58a20ec68599139ed1113b21f977b536a91b42bef3ffed5cce7391"},
|
||||
{file = "mypy-1.7.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d0fa29919d2e720c8dbaf07d5578f93d7b313c3e9954c8ec05b6d83da592e5d9"},
|
||||
{file = "mypy-1.7.0-cp38-cp38-win_amd64.whl", hash = "sha256:2b53655a295c1ed1af9e96b462a736bf083adba7b314ae775563e3fb4e6795f5"},
|
||||
{file = "mypy-1.7.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c1b06b4b109e342f7dccc9efda965fc3970a604db70f8560ddfdee7ef19afb05"},
|
||||
{file = "mypy-1.7.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:bf7a2f0a6907f231d5e41adba1a82d7d88cf1f61a70335889412dec99feeb0f8"},
|
||||
{file = "mypy-1.7.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:551d4a0cdcbd1d2cccdcc7cb516bb4ae888794929f5b040bb51aae1846062901"},
|
||||
{file = "mypy-1.7.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:55d28d7963bef00c330cb6461db80b0b72afe2f3c4e2963c99517cf06454e665"},
|
||||
{file = "mypy-1.7.0-cp39-cp39-win_amd64.whl", hash = "sha256:870bd1ffc8a5862e593185a4c169804f2744112b4a7c55b93eb50f48e7a77010"},
|
||||
{file = "mypy-1.7.0-py3-none-any.whl", hash = "sha256:96650d9a4c651bc2a4991cf46f100973f656d69edc7faf91844e87fe627f7e96"},
|
||||
{file = "mypy-1.7.0.tar.gz", hash = "sha256:1e280b5697202efa698372d2f39e9a6713a0395a756b1c6bd48995f8d72690dc"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@ -902,6 +902,7 @@ typing-extensions = ">=4.1.0"
|
||||
[package.extras]
|
||||
dmypy = ["psutil (>=4.0)"]
|
||||
install-types = ["pip"]
|
||||
mypyc = ["setuptools (>=50)"]
|
||||
reports = ["lxml"]
|
||||
|
||||
[[package]]
|
||||
@ -2188,4 +2189,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p
|
||||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = "^3.9"
|
||||
content-hash = "c822ba4ac16ffd05a4bfbcdb552aacc44e75d62deb4cfdba16771a8b13e14187"
|
||||
content-hash = "5bd0a9ae09f61079a0325639485adb206357cd5ea942944ccb5855f2a83d4db6"
|
||||
|
||||
@ -41,7 +41,7 @@ sphinx = "^7.0.1"
|
||||
furo = "^2023.5.20"
|
||||
black = "^23.3.0"
|
||||
flakehell = "^0.9.0"
|
||||
mypy = "^1.6.0"
|
||||
mypy = "^1.7.0"
|
||||
pdbp = "^1.5.0"
|
||||
|
||||
[build-system]
|
||||
|
||||
Loading…
Reference in New Issue
Block a user