mirror of
https://github.com/beetbox/beets.git
synced 2025-12-06 08:39:17 +01:00
Add NullPathType and types to PathType
This commit is contained in:
parent
1a045c9166
commit
b40ce836d5
5 changed files with 73 additions and 68 deletions
|
|
@ -49,3 +49,5 @@ f36bc497c8c8f89004f3f6879908d3f0b25123e1
|
|||
c490ac5810b70f3cf5fd8649669838e8fdb19f4d
|
||||
# Importer restructure
|
||||
9147577b2b19f43ca827e9650261a86fb0450cef
|
||||
# Copy paste query, types from library to dbcore
|
||||
1a045c91668c771686f4c871c84f1680af2e944b
|
||||
|
|
|
|||
|
|
@ -40,6 +40,8 @@ else:
|
|||
# To use the SQLite "blob" type, it doesn't suffice to provide a byte
|
||||
# string; SQLite treats that as encoded text. Wrapping it in a
|
||||
# `memoryview` tells it that we actually mean non-text data.
|
||||
# needs to be defined in here due to circular import.
|
||||
# TODO: remove it from this module and define it in dbcore/types.py instead
|
||||
BLOB_TYPE = memoryview
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import typing
|
|||
from abc import ABC
|
||||
from typing import TYPE_CHECKING, Any, Generic, TypeVar, cast
|
||||
|
||||
import beets
|
||||
from beets import util
|
||||
|
||||
from . import query
|
||||
|
|
@ -345,7 +346,7 @@ class DateType(Float):
|
|||
return self.null
|
||||
|
||||
|
||||
class PathType(Type[bytes, bytes]):
|
||||
class BasePathType(Type[bytes, N]):
|
||||
"""A dbcore type for filesystem paths.
|
||||
|
||||
These are represented as `bytes` objects, in keeping with
|
||||
|
|
@ -356,27 +357,10 @@ class PathType(Type[bytes, bytes]):
|
|||
query = query.PathQuery
|
||||
model_type = bytes
|
||||
|
||||
def __init__(self, nullable=False):
|
||||
"""Create a path type object.
|
||||
def parse(self, string: str) -> bytes:
|
||||
return util.normpath(string)
|
||||
|
||||
`nullable` controls whether the type may be missing, i.e., None.
|
||||
"""
|
||||
self.nullable = nullable
|
||||
|
||||
@property
|
||||
def null(self):
|
||||
if self.nullable:
|
||||
return None
|
||||
else:
|
||||
return b""
|
||||
|
||||
def format(self, value):
|
||||
return util.displayable_path(value)
|
||||
|
||||
def parse(self, string):
|
||||
return util.normpath(util.bytestring_path(string))
|
||||
|
||||
def normalize(self, value):
|
||||
def normalize(self, value: Any) -> bytes | N:
|
||||
if isinstance(value, str):
|
||||
# Paths stored internally as encoded bytes.
|
||||
return util.bytestring_path(value)
|
||||
|
|
@ -391,12 +375,30 @@ class PathType(Type[bytes, bytes]):
|
|||
def from_sql(self, sql_value):
|
||||
return self.normalize(sql_value)
|
||||
|
||||
def to_sql(self, value):
|
||||
def to_sql(self, value: bytes) -> BLOB_TYPE:
|
||||
if isinstance(value, bytes):
|
||||
value = BLOB_TYPE(value)
|
||||
return value
|
||||
|
||||
|
||||
class NullPathType(BasePathType[None]):
|
||||
@property
|
||||
def null(self) -> None:
|
||||
return None
|
||||
|
||||
def format(self, value: bytes | None) -> str:
|
||||
return util.displayable_path(value or b"")
|
||||
|
||||
|
||||
class PathType(BasePathType[bytes]):
|
||||
@property
|
||||
def null(self) -> bytes:
|
||||
return b""
|
||||
|
||||
def format(self, value: bytes) -> str:
|
||||
return util.displayable_path(value or b"")
|
||||
|
||||
|
||||
class MusicalKey(String):
|
||||
"""String representing the musical key of a song.
|
||||
|
||||
|
|
|
|||
|
|
@ -910,7 +910,7 @@ class Album(LibModel):
|
|||
_always_dirty = True
|
||||
_fields = {
|
||||
"id": types.PRIMARY_ID,
|
||||
"artpath": types.PathType(True),
|
||||
"artpath": types.NullPathType(),
|
||||
"added": types.DATE,
|
||||
"albumartist": types.STRING,
|
||||
"albumartist_sort": types.STRING,
|
||||
|
|
|
|||
|
|
@ -1,59 +1,58 @@
|
|||
import time
|
||||
import unittest
|
||||
|
||||
import beets
|
||||
from beets.dbcore import types
|
||||
from beets.util import normpath
|
||||
|
||||
|
||||
class LibraryFieldTypesTest(unittest.TestCase):
|
||||
"""Test format() and parse() for library-specific field types"""
|
||||
def test_datetype():
|
||||
t = types.DATE
|
||||
|
||||
def test_datetype(self):
|
||||
t = types.DATE
|
||||
# format
|
||||
time_format = beets.config["time_format"].as_str()
|
||||
time_local = time.strftime(time_format, time.localtime(123456789))
|
||||
assert time_local == t.format(123456789)
|
||||
# parse
|
||||
assert 123456789.0 == t.parse(time_local)
|
||||
assert 123456789.0 == t.parse("123456789.0")
|
||||
assert t.null == t.parse("not123456789.0")
|
||||
assert t.null == t.parse("1973-11-29")
|
||||
|
||||
# format
|
||||
time_format = beets.config["time_format"].as_str()
|
||||
time_local = time.strftime(time_format, time.localtime(123456789))
|
||||
assert time_local == t.format(123456789)
|
||||
# parse
|
||||
assert 123456789.0 == t.parse(time_local)
|
||||
assert 123456789.0 == t.parse("123456789.0")
|
||||
assert t.null == t.parse("not123456789.0")
|
||||
assert t.null == t.parse("1973-11-29")
|
||||
|
||||
def test_pathtype(self):
|
||||
t = types.PathType()
|
||||
def test_pathtype():
|
||||
t = types.PathType()
|
||||
|
||||
# format
|
||||
assert "/tmp" == t.format("/tmp")
|
||||
assert "/tmp/\xe4lbum" == t.format("/tmp/\u00e4lbum")
|
||||
# parse
|
||||
assert normpath(b"/tmp") == t.parse("/tmp")
|
||||
assert normpath(b"/tmp/\xc3\xa4lbum") == t.parse("/tmp/\u00e4lbum/")
|
||||
# format
|
||||
assert "/tmp" == t.format("/tmp")
|
||||
assert "/tmp/\xe4lbum" == t.format("/tmp/\u00e4lbum")
|
||||
# parse
|
||||
assert normpath(b"/tmp") == t.parse("/tmp")
|
||||
assert normpath(b"/tmp/\xc3\xa4lbum") == t.parse("/tmp/\u00e4lbum/")
|
||||
|
||||
def test_musicalkey(self):
|
||||
t = types.MusicalKey()
|
||||
|
||||
# parse
|
||||
assert "C#m" == t.parse("c#m")
|
||||
assert "Gm" == t.parse("g minor")
|
||||
assert "Not c#m" == t.parse("not C#m")
|
||||
def test_musicalkey():
|
||||
t = types.MusicalKey()
|
||||
|
||||
def test_durationtype(self):
|
||||
t = types.DurationType()
|
||||
# parse
|
||||
assert "C#m" == t.parse("c#m")
|
||||
assert "Gm" == t.parse("g minor")
|
||||
assert "Not c#m" == t.parse("not C#m")
|
||||
|
||||
# format
|
||||
assert "1:01" == t.format(61.23)
|
||||
assert "60:01" == t.format(3601.23)
|
||||
assert "0:00" == t.format(None)
|
||||
# parse
|
||||
assert 61.0 == t.parse("1:01")
|
||||
assert 61.23 == t.parse("61.23")
|
||||
assert 3601.0 == t.parse("60:01")
|
||||
assert t.null == t.parse("1:00:01")
|
||||
assert t.null == t.parse("not61.23")
|
||||
# config format_raw_length
|
||||
beets.config["format_raw_length"] = True
|
||||
assert 61.23 == t.format(61.23)
|
||||
assert 3601.23 == t.format(3601.23)
|
||||
|
||||
def test_durationtype():
|
||||
t = types.DurationType()
|
||||
|
||||
# format
|
||||
assert "1:01" == t.format(61.23)
|
||||
assert "60:01" == t.format(3601.23)
|
||||
assert "0:00" == t.format(None)
|
||||
# parse
|
||||
assert 61.0 == t.parse("1:01")
|
||||
assert 61.23 == t.parse("61.23")
|
||||
assert 3601.0 == t.parse("60:01")
|
||||
assert t.null == t.parse("1:00:01")
|
||||
assert t.null == t.parse("not61.23")
|
||||
# config format_raw_length
|
||||
beets.config["format_raw_length"] = True
|
||||
assert 61.23 == t.format(61.23)
|
||||
assert 3601.23 == t.format(3601.23)
|
||||
|
|
|
|||
Loading…
Reference in a new issue