handle XDG_CONFIG_HOME specification ...

+ fix bug that caused duplicate artist color entries to db
+ check if app is windows (prep for windows build)
+ remove caribou migrations lib
+ rename all api blueprints to "api"
+ unregister child directories when customizing root dirs
+ misc
This commit is contained in:
geoffrey45 2023-01-22 23:57:12 +03:00
parent 4e6e1f03dc
commit bcc4873766
18 changed files with 163 additions and 179 deletions

View File

@ -5,17 +5,8 @@ This module combines all API blueprints into a single Flask app instance.
from flask import Flask
from flask_cors import CORS
from app.api import (
album,
artist,
favorites,
folder,
playlist,
search,
track,
settings,
imgserver,
)
from app.api import (album, artist, favorites, folder, imgserver, playlist,
search, settings, track)
def create_api():
@ -27,14 +18,14 @@ def create_api():
with app.app_context():
app.register_blueprint(album.albumbp)
app.register_blueprint(artist.artistbp)
app.register_blueprint(track.trackbp)
app.register_blueprint(search.searchbp)
app.register_blueprint(folder.folderbp)
app.register_blueprint(playlist.playlistbp)
app.register_blueprint(favorites.favbp)
app.register_blueprint(imgserver.imgbp)
app.register_blueprint(settings.settingsbp)
app.register_blueprint(album.api)
app.register_blueprint(artist.api)
app.register_blueprint(track.api)
app.register_blueprint(search.api)
app.register_blueprint(folder.api)
app.register_blueprint(playlist.api)
app.register_blueprint(favorites.api)
app.register_blueprint(imgserver.api)
app.register_blueprint(settings.api)
return app

View File

@ -12,15 +12,14 @@ from app.db.sqlite.favorite import SQLiteFavoriteMethods as favdb
from app.db.store import Store
from app.models import FavType, Track
get_album_by_id = adb.get_album_by_id
get_albums_by_albumartist = adb.get_albums_by_albumartist
check_is_fav = favdb.check_is_favorite
albumbp = Blueprint("album", __name__, url_prefix="")
api = Blueprint("album", __name__, url_prefix="")
@albumbp.route("/album", methods=["POST"])
@api.route("/album", methods=["POST"])
def get_album():
"""Returns all the tracks in the given album."""
@ -88,7 +87,7 @@ def get_album():
return {"tracks": tracks, "info": album}
@albumbp.route("/album/<albumhash>/tracks", methods=["GET"])
@api.route("/album/<albumhash>/tracks", methods=["GET"])
def get_album_tracks(albumhash: str):
"""
Returns all the tracks in the given album.
@ -105,7 +104,7 @@ def get_album_tracks(albumhash: str):
return {"tracks": tracks}
@albumbp.route("/album/from-artist", methods=["POST"])
@api.route("/album/from-artist", methods=["POST"])
def get_artist_albums():
data = request.get_json()

View File

@ -5,12 +5,12 @@ from collections import deque
from flask import Blueprint, request
from app.db.sqlite.favorite import SQLiteFavoriteMethods as favdb
from app.db.store import Store
from app.models import Album, FavType, Track
from app.utils import remove_duplicates
from app.db.sqlite.favorite import SQLiteFavoriteMethods as favdb
artistbp = Blueprint("artist", __name__, url_prefix="/")
api = Blueprint("artist", __name__, url_prefix="/")
class CacheEntry:
@ -156,7 +156,7 @@ def add_albums_to_cache(artisthash: str):
# =======================================================
@artistbp.route("/artist/<artisthash>", methods=["GET"])
@api.route("/artist/<artisthash>", methods=["GET"])
def get_artist(artisthash: str):
"""
Get artist data.
@ -203,7 +203,7 @@ def get_artist(artisthash: str):
return {"artist": artist, "tracks": tracks[:limit]}
@artistbp.route("/artist/<artisthash>/albums", methods=["GET"])
@api.route("/artist/<artisthash>/albums", methods=["GET"])
def get_artist_albums(artisthash: str):
limit = request.args.get("limit")
@ -261,7 +261,7 @@ def get_artist_albums(artisthash: str):
}
@artistbp.route("/artist/<artisthash>/tracks", methods=["GET"])
@api.route("/artist/<artisthash>/tracks", methods=["GET"])
def get_artist_tracks(artisthash: str):
"""
Returns all artists by a given artist.

View File

@ -1,17 +1,18 @@
from flask import Blueprint, request
from app.db.sqlite.favorite import SQLiteFavoriteMethods as favdb
from app.db.store import Store
from app.models import FavType
from app.utils import UseBisection
favbp = Blueprint("favorite", __name__, url_prefix="/")
api = Blueprint("favorite", __name__, url_prefix="/")
def remove_none(items: list):
return [i for i in items if i is not None]
@favbp.route("/favorite/add", methods=["POST"])
@api.route("/favorite/add", methods=["POST"])
def add_favorite():
"""
Adds a favorite to the database.
@ -32,7 +33,7 @@ def add_favorite():
return {"msg": "Added to favorites"}
@favbp.route("/favorite/remove", methods=["POST"])
@api.route("/favorite/remove", methods=["POST"])
def remove_favorite():
"""
Removes a favorite from the database.
@ -53,7 +54,7 @@ def remove_favorite():
return {"msg": "Removed from favorites"}
@favbp.route("/albums/favorite")
@api.route("/albums/favorite")
def get_favorite_albums():
limit = request.args.get("limit")
@ -77,7 +78,7 @@ def get_favorite_albums():
return {"albums": fav_albums[:limit]}
@favbp.route("/tracks/favorite")
@api.route("/tracks/favorite")
def get_favorite_tracks():
limit = request.args.get("limit")
@ -100,7 +101,7 @@ def get_favorite_tracks():
return {"tracks": tracks[:limit]}
@favbp.route("/artists/favorite")
@api.route("/artists/favorite")
def get_favorite_artists():
limit = request.args.get("limit")
@ -124,7 +125,7 @@ def get_favorite_artists():
return {"artists": artists[:limit]}
@favbp.route("/favorites")
@api.route("/favorites")
def get_all_favorites():
"""
Returns all the favorites in the database.
@ -191,7 +192,7 @@ def get_all_favorites():
}
@favbp.route("/favorites/check")
@api.route("/favorites/check")
def check_favorite():
"""
Checks if a favorite exists in the database.

View File

@ -2,16 +2,16 @@
Contains all the folder routes.
"""
import os
from flask import Blueprint, request
from app import settings
from app.lib.folderslib import GetFilesAndDirs
folderbp = Blueprint("folder", __name__, url_prefix="/")
api = Blueprint("folder", __name__, url_prefix="/")
@folderbp.route("/folder", methods=["POST"])
@api.route("/folder", methods=["POST"])
def get_folder_tree():
"""
Returns a list of all the folders and tracks in the given folder.
@ -21,10 +21,10 @@ def get_folder_tree():
if data is not None:
req_dir: str = data["folder"]
else:
req_dir = settings.HOME_DIR
req_dir = settings.USER_HOME_DIR
if req_dir == "$home":
req_dir = settings.HOME_DIR
req_dir = settings.USER_HOME_DIR
tracks, folders = GetFilesAndDirs(req_dir)()
@ -34,7 +34,7 @@ def get_folder_tree():
}
@folderbp.route("/folder/dir-browser", methods=["POST"])
@api.route("/folder/dir-browser", methods=["POST"])
def list_folders():
"""
Returns a list of all the folders in the given folder.
@ -44,10 +44,10 @@ def list_folders():
try:
req_dir: str = data["folder"]
except KeyError:
req_dir = settings.HOME_DIR
req_dir = settings.USER_HOME_DIR
if req_dir == "$home":
req_dir = settings.HOME_DIR
req_dir = settings.USER_HOME_DIR
entries = os.scandir(req_dir)

View File

@ -1,14 +1,13 @@
import os
from pathlib import Path
from flask import Blueprint, send_from_directory
imgbp = Blueprint("imgserver", __name__, url_prefix="/img")
from app.settings import APP_DIR
api = Blueprint("imgserver", __name__, url_prefix="/img")
SUPPORTED_IMAGES = (".jpg", ".png", ".webp", ".jpeg")
HOME = os.path.expanduser("~")
APP_DIR = Path(HOME) / ".swing"
APP_DIR = Path(APP_DIR)
IMG_PATH = APP_DIR / "images"
ASSETS_PATH = APP_DIR / "assets"
@ -23,7 +22,7 @@ ARTIST_SM_PATH = ARTIST_PATH / "small"
PLAYLIST_PATH = IMG_PATH / "playlists"
@imgbp.route("/")
@api.route("/")
def hello():
return "<h1>Image Server</h1>"
@ -37,7 +36,7 @@ def send_fallback_img(filename: str = "default.webp"):
return send_from_directory(ASSETS_PATH, filename)
@imgbp.route("/t/<imgpath>")
@api.route("/t/<imgpath>")
def send_lg_thumbnail(imgpath: str):
fpath = LG_THUMB_PATH / imgpath
@ -47,7 +46,7 @@ def send_lg_thumbnail(imgpath: str):
return send_fallback_img()
@imgbp.route("/t/s/<imgpath>")
@api.route("/t/s/<imgpath>")
def send_sm_thumbnail(imgpath: str):
fpath = SM_THUMB_PATH / imgpath
@ -57,7 +56,7 @@ def send_sm_thumbnail(imgpath: str):
return send_fallback_img()
@imgbp.route("/a/<imgpath>")
@api.route("/a/<imgpath>")
def send_lg_artist_image(imgpath: str):
fpath = ARTIST_LG_PATH / imgpath
@ -67,7 +66,7 @@ def send_lg_artist_image(imgpath: str):
return send_fallback_img("artist.webp")
@imgbp.route("/a/s/<imgpath>")
@api.route("/a/s/<imgpath>")
def send_sm_artist_image(imgpath: str):
fpath = ARTIST_SM_PATH / imgpath
@ -77,7 +76,7 @@ def send_sm_artist_image(imgpath: str):
return send_fallback_img("artist.webp")
@imgbp.route("/p/<imgpath>")
@api.route("/p/<imgpath>")
def send_playlist_image(imgpath: str):
fpath = PLAYLIST_PATH / imgpath

View File

@ -13,7 +13,7 @@ from app.db.store import Store
from app.lib import playlistlib
from app.utils import create_new_date, remove_duplicates
playlistbp = Blueprint("playlist", __name__, url_prefix="/")
api = Blueprint("playlist", __name__, url_prefix="/")
PL = SQLitePlaylistMethods
@ -30,7 +30,7 @@ delete_playlist = PL.delete_playlist
# get_tracks_by_trackhashes = SQLiteTrackMethods.get_tracks_by_trackhashes
@playlistbp.route("/playlists", methods=["GET"])
@api.route("/playlists", methods=["GET"])
def send_all_playlists():
"""
Gets all the playlists.
@ -46,7 +46,7 @@ def send_all_playlists():
return {"data": playlists}
@playlistbp.route("/playlist/new", methods=["POST"])
@api.route("/playlist/new", methods=["POST"])
def create_playlist():
"""
Creates a new playlist. Accepts POST method with a JSON body.
@ -79,7 +79,7 @@ def create_playlist():
return {"playlist": playlist}, 201
@playlistbp.route("/playlist/<playlist_id>/add", methods=["POST"])
@api.route("/playlist/<playlist_id>/add", methods=["POST"])
def add_track_to_playlist(playlist_id: str):
"""
Takes a playlist ID and a track hash, and adds the track to the playlist
@ -102,7 +102,7 @@ def add_track_to_playlist(playlist_id: str):
return {"msg": "Done"}, 200
@playlistbp.route("/playlist/<playlistid>")
@api.route("/playlist/<playlistid>")
def get_playlist(playlistid: str):
"""
Gets a playlist by id, and if it exists, it gets all the tracks in the playlist and returns them.
@ -123,7 +123,7 @@ def get_playlist(playlistid: str):
return {"info": playlist, "tracks": tracks}
@playlistbp.route("/playlist/<playlistid>/update", methods=["PUT"])
@api.route("/playlist/<playlistid>/update", methods=["PUT"])
def update_playlist_info(playlistid: str):
if playlistid is None:
return {"error": "Playlist ID not provided"}, 400
@ -188,7 +188,7 @@ def update_playlist_info(playlistid: str):
# return {"data": artists}
@playlistbp.route("/playlist/delete", methods=["POST"])
@api.route("/playlist/delete", methods=["POST"])
def remove_playlist():
"""
Deletes a playlist by ID.
@ -209,7 +209,7 @@ def remove_playlist():
return {"msg": "Done"}, 200
@playlistbp.route("/playlist/<pid>/set-image-pos", methods=["POST"])
@api.route("/playlist/<pid>/set-image-pos", methods=["POST"])
def update_image_position(pid: int):
data = request.get_json()
message = {"msg": "No data provided"}

View File

@ -8,7 +8,7 @@ from app import models, utils
from app.db.store import Store
from app.lib import searchlib
searchbp = Blueprint("search", __name__, url_prefix="/")
api = Blueprint("search", __name__, url_prefix="/")
SEARCH_COUNT = 12
@ -95,7 +95,7 @@ class DoSearch:
# self.search_playlists()
@searchbp.route("/search/tracks", methods=["GET"])
@api.route("/search/tracks", methods=["GET"])
def search_tracks():
"""
Searches for tracks that match the search query.
@ -113,7 +113,7 @@ def search_tracks():
}
@searchbp.route("/search/albums", methods=["GET"])
@api.route("/search/albums", methods=["GET"])
def search_albums():
"""
Searches for albums.
@ -131,7 +131,7 @@ def search_albums():
}
@searchbp.route("/search/artists", methods=["GET"])
@api.route("/search/artists", methods=["GET"])
def search_artists():
"""
Searches for artists.
@ -167,7 +167,7 @@ def search_artists():
# }
@searchbp.route("/search/top", methods=["GET"])
@api.route("/search/top", methods=["GET"])
def get_top_results():
"""
Returns the top results for the search query.
@ -188,7 +188,7 @@ def get_top_results():
}
@searchbp.route("/search/loadmore")
@api.route("/search/loadmore")
def search_load_more():
"""
Returns more songs, albums or artists from a search query.

View File

@ -1,10 +1,17 @@
from flask import Blueprint, request
from app.db.sqlite.settings import SettingsSQLMethods as sdb
settingsbp = Blueprint("settings", __name__, url_prefix="/")
api = Blueprint("settings", __name__, url_prefix="/")
@settingsbp.route("/settings/add-root-dirs", methods=["POST"])
def get_child_dirs(parent: str, children: list[str]):
"""Returns child directories in a list, given a parent directory"""
return [dir for dir in children if dir.startswith(parent)]
@api.route("/settings/add-root-dirs", methods=["POST"])
def add_root_dirs():
"""
Add custom root directories to the database.
@ -17,18 +24,26 @@ def add_root_dirs():
return msg, 400
try:
new_dirs = data["new_dirs"]
removed_dirs = data["removed"]
new_dirs: list[str] = data["new_dirs"]
removed_dirs: list[str] = data["removed"]
except KeyError:
return msg, 400
# --- Unregister child directories ---
db_dirs = sdb.get_root_dirs()
for _dir in new_dirs:
children = get_child_dirs(_dir, db_dirs)
removed_dirs.extend(children)
# ------------------------------------
sdb.add_root_dirs(new_dirs)
sdb.remove_root_dirs(removed_dirs)
return {"msg": "Added root directories to the database."}
return {"msg": "Updated!"}
@settingsbp.route("/settings/get-root-dirs", methods=["GET"])
@api.route("/settings/get-root-dirs", methods=["GET"])
def get_root_dirs():
"""
Get custom root directories from the database.
@ -39,7 +54,7 @@ def get_root_dirs():
# CURRENTLY UNUSED ROUTE 👇
@settingsbp.route("/settings/remove-root-dirs", methods=["POST"])
@api.route("/settings/remove-root-dirs", methods=["POST"])
def remove_root_dirs():
"""
Remove custom root directories from the database.

View File

@ -2,12 +2,13 @@
Contains all the track routes.
"""
from flask import Blueprint, send_file
from app.db.store import Store
trackbp = Blueprint("track", __name__, url_prefix="/")
api = Blueprint("track", __name__, url_prefix="/")
@trackbp.route("/file/<trackhash>")
@api.route("/file/<trackhash>")
def send_track_file(trackhash: str):
"""
Returns an audio file that matches the passed id to the client.

View File

@ -69,13 +69,12 @@ class ProcessArtistColors:
"""
def __init__(self) -> None:
db_colors: list[tuple] = list(adb.get_all_artists())
db_artisthashes = "-".join([artist[1] for artist in db_colors])
all_artists = Store.artists
if all_artists is None:
return
for artist in tqdm(all_artists, desc="Processing artist colors"):
if len(artist.colors) == 0:
if artist.artisthash not in db_artisthashes:
self.process_color(artist)
@staticmethod
@ -90,5 +89,3 @@ class ProcessArtistColors:
if len(colors) > 0:
adb.insert_one_artist(artisthash=artist.artisthash, colors=colors)
Store.map_artist_color((0, artist.artisthash, json.dumps(colors)))
# TODO: Load album and artist colors into the store.

View File

@ -0,0 +1,3 @@
"""
Migrations module
"""

View File

@ -1,16 +1,42 @@
"""
Contains default configs
"""
import multiprocessing
import os
# ------- HELPER METHODS --------
def get_xdg_config_dir():
"""
Returns the XDG_CONFIG_HOME environment variable if it exists, otherwise
returns the default config directory. If none of those exist, returns the
user's home directory.
"""
xdg_config_home = os.environ.get("XDG_CONFIG_HOME")
if xdg_config_home:
return xdg_config_home
try:
alt_dir = os.path.join(os.environ.get("HOME"), ".config")
if os.path.exists(alt_dir):
return alt_dir
except TypeError:
return os.path.expanduser("~")
# ------- HELPER METHODS --------
APP_VERSION = "Swing v.1.0.0.beta.1"
# paths
CONFIG_FOLDER = ".swing"
HOME_DIR = os.path.expanduser("~")
XDG_CONFIG_DIR = get_xdg_config_dir()
USER_HOME_DIR = os.path.expanduser("~")
APP_DIR = os.path.join(HOME_DIR, CONFIG_FOLDER)
CONFIG_FOLDER = "swing" if XDG_CONFIG_DIR != USER_HOME_DIR else ".swing"
APP_DIR = os.path.join(XDG_CONFIG_DIR, CONFIG_FOLDER)
IMG_PATH = os.path.join(APP_DIR, "images")
ARTIST_IMG_PATH = os.path.join(IMG_PATH, "artists")
@ -37,8 +63,6 @@ DEFAULT_ARTIST_IMG = IMG_ARTIST_URI + "0.webp"
LAST_FM_API_KEY = "762db7a44a9e6fb5585661f5f2bdf23a"
CPU_COUNT = multiprocessing.cpu_count()
THUMB_SIZE = 400
SM_THUMB_SIZE = 64
SM_ARTIST_IMG_SIZE = 64
@ -46,24 +70,9 @@ SM_ARTIST_IMG_SIZE = 64
The size of extracted images in pixels
"""
LOGGER_ENABLE: bool = True
FILES = ["flac", "mp3", "wav", "m4a"]
SUPPORTED_FILES = tuple(f".{file}" for file in FILES)
SUPPORTED_IMAGES = (".jpg", ".png", ".webp", ".jpeg")
SUPPORTED_DIR_IMAGES = [
"folder",
"cover",
"album",
"front",
]
# ===== DB =========
USE_MONGO = False
# ===== SQLite =====
APP_DB_NAME = "swing.db"
USER_DATA_DB_NAME = "userdata.db"

View File

@ -4,7 +4,6 @@ Contains the functions to prepare the server for use.
import os
import shutil
from configparser import ConfigParser
import caribou # pylint: disable=import-error
from app import settings
from app.db.sqlite import create_connection, create_tables, queries
@ -64,10 +63,6 @@ def create_config_dir() -> None:
"""
Creates the config directory if it doesn't exist.
"""
home_dir = os.path.expanduser("~")
config_folder = os.path.join(home_dir, settings.CONFIG_FOLDER)
thumb_path = os.path.join("images", "thumbnails")
small_thumb_path = os.path.join(thumb_path, "small")
large_thumb_path = os.path.join(thumb_path, "large")
@ -91,7 +86,7 @@ def create_config_dir() -> None:
]
for _dir in dirs:
path = os.path.join(config_folder, _dir)
path = os.path.join(settings.APP_DIR, _dir)
exists = os.path.exists(path)
if not exists:
@ -114,19 +109,6 @@ def setup_sqlite():
create_tables(app_db_conn, queries.CREATE_APPDB_TABLES)
create_tables(playlist_db_conn, queries.CREATE_USERDATA_TABLES)
userdb_migrations = get_home_res_path("app") / "migrations" / "userdata"
maindb_migrations = get_home_res_path("app") / "migrations" / "main"
caribou.upgrade(
APP_DB_PATH,
maindb_migrations,
)
caribou.upgrade(
str(USERDATA_DB_PATH),
str(userdb_migrations),
)
app_db_conn.close()
playlist_db_conn.close()

View File

@ -5,6 +5,7 @@ from pathlib import Path
from datetime import datetime
import os
import platform
import socket as Socket
import hashlib
import threading
@ -38,19 +39,22 @@ def run_fast_scandir(__dir: str, full=False) -> tuple[list[str], list[str]]:
subfolders = []
files = []
for f in os.scandir(__dir):
if f.is_dir() and not f.name.startswith("."):
subfolders.append(f.path)
if f.is_file():
ext = os.path.splitext(f.name)[1].lower()
try:
for _files in os.scandir(__dir):
if _files.is_dir() and not _files.name.startswith("."):
subfolders.append(_files.path)
if _files.is_file():
ext = os.path.splitext(_files.name)[1].lower()
if ext in SUPPORTED_FILES:
files.append(f.path)
files.append(_files.path)
if full or len(files) == 0:
for _dir in list(subfolders):
sf, f = run_fast_scandir(_dir, full=True)
subfolders.extend(sf)
files.extend(f)
sub_dirs, _files = run_fast_scandir(_dir, full=True)
subfolders.extend(sub_dirs)
files.extend(_files)
except PermissionError:
return [], []
return subfolders, files
@ -239,3 +243,10 @@ def get_ip():
soc.close()
return ip_address
def is_windows():
"""
Returns True if the OS is Windows.
"""
return platform.system() == "Windows"

View File

@ -13,7 +13,7 @@ from app.functions import run_periodic_checks
from app.lib.watchdogg import Watcher as WatchDog
from app.settings import APP_VERSION, HELP_MESSAGE, TCOLOR
from app.setup import run_setup
from app.utils import background, get_home_res_path, get_ip
from app.utils import background, get_home_res_path, get_ip, is_windows
werkzeug = logging.getLogger("werkzeug")
werkzeug.setLevel(logging.ERROR)
@ -80,6 +80,8 @@ class HandleArgs:
config["DEFAULT"]["BUILD"] = "True"
config.write(file)
_s = ";" if is_windows() else ":"
bundler.run(
[
"manage.py",
@ -87,9 +89,10 @@ class HandleArgs:
"--name",
"swingmusic",
"--clean",
"--add-data=assets:assets",
"--add-data=client:client",
"--add-data=pyinstaller.config.ini:.",
f"--add-data=assets{_s}assets",
f"--add-data=client{_s}client",
f"--add-data=app/migrations{_s}app/migrations",
f"--add-data=pyinstaller.config.ini{_s}.",
"-y",
]
)

46
poetry.lock generated
View File

@ -12,23 +12,11 @@ files = [
{file = "altgraph-0.17.3.tar.gz", hash = "sha256:ad33358114df7c9416cdb8fa1eaa5852166c505118717021c6a8c7c7abbd03dd"},
]
[[package]]
name = "argparse"
version = "1.4.0"
description = "Python command-line parsing library"
category = "main"
optional = false
python-versions = "*"
files = [
{file = "argparse-1.4.0-py2.py3-none-any.whl", hash = "sha256:c31647edb69fd3d465a847ea3157d37bed1f95f19760b11a47aa91c04b666314"},
{file = "argparse-1.4.0.tar.gz", hash = "sha256:62b089a55be1d8949cd2bc7e0df0bddb9e028faefc8c32038cc84862aefdd6e4"},
]
[[package]]
name = "astroid"
version = "2.12.12"
description = "An abstract syntax tree for Python with inference support."
category = "main"
category = "dev"
optional = false
python-versions = ">=3.7.2"
files = [
@ -107,20 +95,6 @@ d = ["aiohttp (>=3.7.4)"]
jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"]
uvloop = ["uvloop (>=0.15.2)"]
[[package]]
name = "caribou"
version = "0.3"
description = "python migrations for sqlite databases"
category = "main"
optional = false
python-versions = "*"
files = [
{file = "caribou-0.3.0.tar.gz", hash = "sha256:5ca6e6e6ad7d3175137c68d809e203fbd931f79163a5613808a789449fef7863"},
]
[package.dependencies]
argparse = ">=1.0.0"
[[package]]
name = "certifi"
version = "2022.5.18.1"
@ -194,7 +168,7 @@ pillow = ">=3.3.1"
name = "dill"
version = "0.3.6"
description = "serialize all of python"
category = "main"
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
@ -351,7 +325,7 @@ files = [
name = "isort"
version = "5.10.1"
description = "A Python utility / library to sort Python imports."
category = "main"
category = "dev"
optional = false
python-versions = ">=3.6.1,<4.0"
files = [
@ -399,7 +373,7 @@ i18n = ["Babel (>=2.7)"]
name = "lazy-object-proxy"
version = "1.8.0"
description = "A fast and thorough lazy object proxy."
category = "main"
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
@ -493,7 +467,7 @@ files = [
name = "mccabe"
version = "0.7.0"
description = "McCabe checker, plugin for flake8"
category = "main"
category = "dev"
optional = false
python-versions = ">=3.6"
files = [
@ -630,7 +604,7 @@ tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "pa
name = "platformdirs"
version = "2.5.2"
description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
category = "main"
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
@ -720,7 +694,7 @@ files = [
name = "pylint"
version = "2.15.5"
description = "python code static checker"
category = "main"
category = "dev"
optional = false
python-versions = ">=3.7.2"
files = [
@ -988,7 +962,7 @@ files = [
name = "tomlkit"
version = "0.11.6"
description = "Style preserving TOML library"
category = "main"
category = "dev"
optional = false
python-versions = ">=3.6"
files = [
@ -1106,7 +1080,7 @@ watchdog = ["watchdog"]
name = "wrapt"
version = "1.14.1"
description = "Module for decorators, wrappers and monkey patching."
category = "main"
category = "dev"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
files = [
@ -1179,4 +1153,4 @@ files = [
[metadata]
lock-version = "2.0"
python-versions = ">=3.10,<3.12"
content-hash = "83ce6d95a8e6bdefff93a3badca9e7b7b53174eaea85f908e9cea9a37afaec7c"
content-hash = "6f6126fbaa8f114c90cef637e0bc996d63d3dc93f6ed2e90e36ef696cdd0cfc5"

View File

@ -20,7 +20,6 @@ hypothesis = "^6.56.3"
pytest = "^7.1.3"
Unidecode = "^1.3.6"
pyinstaller = "^5.7.0"
caribou = "^0.3.0"
[tool.poetry.dev-dependencies]
pylint = "^2.15.5"