Cleaned up the directories

This commit is contained in:
ComputerTech312 2024-02-19 15:34:25 +01:00
parent f708506d68
commit a683fcffea
1340 changed files with 554582 additions and 6840 deletions

View file

@ -0,0 +1,5 @@
from .country import Country # noqa
from .currency import Currency # noqa
from .ltree import Ltree # noqa
from .weekday import WeekDay # noqa
from .weekdays import WeekDays # noqa

View file

@ -0,0 +1,110 @@
from functools import total_ordering
from .. import i18n
from ..utils import str_coercible
@total_ordering
@str_coercible
class Country:
"""
Country class wraps a 2 to 3 letter country code. It provides various
convenience properties and methods.
::
from babel import Locale
from sqlalchemy_utils import Country, i18n
# First lets add a locale getter for testing purposes
i18n.get_locale = lambda: Locale('en')
Country('FI').name # Finland
Country('FI').code # FI
Country(Country('FI')).code # 'FI'
Country always validates the given code if you use at least the optional
dependency list 'babel', otherwise no validation are performed.
::
Country(None) # raises TypeError
Country('UnknownCode') # raises ValueError
Country supports equality operators.
::
Country('FI') == Country('FI')
Country('FI') != Country('US')
Country objects are hashable.
::
assert hash(Country('FI')) == hash('FI')
"""
def __init__(self, code_or_country):
if isinstance(code_or_country, Country):
self.code = code_or_country.code
elif isinstance(code_or_country, str):
self.validate(code_or_country)
self.code = code_or_country
else:
raise TypeError(
"Country() argument must be a string or a country, not '{}'"
.format(
type(code_or_country).__name__
)
)
@property
def name(self):
return i18n.get_locale().territories[self.code]
@classmethod
def validate(self, code):
try:
i18n.babel.Locale('en').territories[code]
except KeyError:
raise ValueError(
f'Could not convert string to country code: {code}'
)
except AttributeError:
# As babel is optional, we may raise an AttributeError accessing it
pass
def __eq__(self, other):
if isinstance(other, Country):
return self.code == other.code
elif isinstance(other, str):
return self.code == other
else:
return NotImplemented
def __hash__(self):
return hash(self.code)
def __ne__(self, other):
return not (self == other)
def __lt__(self, other):
if isinstance(other, Country):
return self.code < other.code
elif isinstance(other, str):
return self.code < other
return NotImplemented
def __repr__(self):
return f'{self.__class__.__name__}({self.code!r})'
def __unicode__(self):
return self.name

View file

@ -0,0 +1,109 @@
from .. import i18n, ImproperlyConfigured
from ..utils import str_coercible
@str_coercible
class Currency:
"""
Currency class wraps a 3-letter currency code. It provides various
convenience properties and methods.
::
from babel import Locale
from sqlalchemy_utils import Currency, i18n
# First lets add a locale getter for testing purposes
i18n.get_locale = lambda: Locale('en')
Currency('USD').name # US Dollar
Currency('USD').symbol # $
Currency(Currency('USD')).code # 'USD'
Currency always validates the given code if you use at least the optional
dependency list 'babel', otherwise no validation are performed.
::
Currency(None) # raises TypeError
Currency('UnknownCode') # raises ValueError
Currency supports equality operators.
::
Currency('USD') == Currency('USD')
Currency('USD') != Currency('EUR')
Currencies are hashable.
::
len(set([Currency('USD'), Currency('USD')])) # 1
"""
def __init__(self, code):
if i18n.babel is None:
raise ImproperlyConfigured(
"'babel' package is required in order to use Currency class."
)
if isinstance(code, Currency):
self.code = code
elif isinstance(code, str):
self.validate(code)
self.code = code
else:
raise TypeError(
'First argument given to Currency constructor should be '
'either an instance of Currency or valid three letter '
'currency code.'
)
@classmethod
def validate(self, code):
try:
i18n.babel.Locale('en').currencies[code]
except KeyError:
raise ValueError(f"'{code}' is not valid currency code.")
except AttributeError:
# As babel is optional, we may raise an AttributeError accessing it
pass
@property
def symbol(self):
return i18n.babel.numbers.get_currency_symbol(
self.code,
i18n.get_locale()
)
@property
def name(self):
return i18n.get_locale().currencies[self.code]
def __eq__(self, other):
if isinstance(other, Currency):
return self.code == other.code
elif isinstance(other, str):
return self.code == other
else:
return NotImplemented
def __ne__(self, other):
return not (self == other)
def __hash__(self):
return hash(self.code)
def __repr__(self):
return f'{self.__class__.__name__}({self.code!r})'
def __unicode__(self):
return self.code

View file

@ -0,0 +1,220 @@
import re
from ..utils import str_coercible
path_matcher = re.compile(r'^[A-Za-z0-9_]+(\.[A-Za-z0-9_]+)*$')
@str_coercible
class Ltree:
"""
Ltree class wraps a valid string label path. It provides various
convenience properties and methods.
::
from sqlalchemy_utils import Ltree
Ltree('1.2.3').path # '1.2.3'
Ltree always validates the given path.
::
Ltree(None) # raises TypeError
Ltree('..') # raises ValueError
Validator is also available as class method.
::
Ltree.validate('1.2.3')
Ltree.validate(None) # raises TypeError
Ltree supports equality operators.
::
Ltree('Countries.Finland') == Ltree('Countries.Finland')
Ltree('Countries.Germany') != Ltree('Countries.Finland')
Ltree objects are hashable.
::
assert hash(Ltree('Finland')) == hash('Finland')
Ltree objects have length.
::
assert len(Ltree('1.2')) == 2
assert len(Ltree('some.one.some.where')) # 4
You can easily find subpath indexes.
::
assert Ltree('1.2.3').index('2.3') == 1
assert Ltree('1.2.3.4.5').index('3.4') == 2
Ltree objects can be sliced.
::
assert Ltree('1.2.3')[0:2] == Ltree('1.2')
assert Ltree('1.2.3')[1:] == Ltree('2.3')
Finding longest common ancestor.
::
assert Ltree('1.2.3.4.5').lca('1.2.3', '1.2.3.4', '1.2.3') == '1.2'
assert Ltree('1.2.3.4.5').lca('1.2', '1.2.3') == '1'
Ltree objects can be concatenated.
::
assert Ltree('1.2') + Ltree('1.2') == Ltree('1.2.1.2')
"""
def __init__(self, path_or_ltree):
if isinstance(path_or_ltree, Ltree):
self.path = path_or_ltree.path
elif isinstance(path_or_ltree, str):
self.validate(path_or_ltree)
self.path = path_or_ltree
else:
raise TypeError(
"Ltree() argument must be a string or an Ltree, not '{}'"
.format(
type(path_or_ltree).__name__
)
)
@classmethod
def validate(cls, path):
if path_matcher.match(path) is None:
raise ValueError(
f"'{path}' is not a valid ltree path."
)
def __len__(self):
return len(self.path.split('.'))
def index(self, other):
subpath = Ltree(other).path.split('.')
parts = self.path.split('.')
for index, _ in enumerate(parts):
if parts[index:len(subpath) + index] == subpath:
return index
raise ValueError('subpath not found')
def descendant_of(self, other):
"""
is left argument a descendant of right (or equal)?
::
assert Ltree('1.2.3.4.5').descendant_of('1.2.3')
"""
subpath = self[:len(Ltree(other))]
return subpath == other
def ancestor_of(self, other):
"""
is left argument an ancestor of right (or equal)?
::
assert Ltree('1.2.3').ancestor_of('1.2.3.4.5')
"""
subpath = Ltree(other)[:len(self)]
return subpath == self
def __getitem__(self, key):
if isinstance(key, int):
return Ltree(self.path.split('.')[key])
elif isinstance(key, slice):
return Ltree('.'.join(self.path.split('.')[key]))
raise TypeError(
'Ltree indices must be integers, not {}'.format(
key.__class__.__name__
)
)
def lca(self, *others):
"""
Lowest common ancestor, i.e., longest common prefix of paths
::
assert Ltree('1.2.3.4.5').lca('1.2.3', '1.2.3.4', '1.2.3') == '1.2'
"""
other_parts = [Ltree(other).path.split('.') for other in others]
parts = self.path.split('.')
for index, element in enumerate(parts):
if any(
other[index] != element or
len(other) <= index + 1 or
len(parts) == index + 1
for other in other_parts
):
if index == 0:
return None
return Ltree('.'.join(parts[0:index]))
def __add__(self, other):
return Ltree(self.path + '.' + Ltree(other).path)
def __radd__(self, other):
return Ltree(other) + self
def __eq__(self, other):
if isinstance(other, Ltree):
return self.path == other.path
elif isinstance(other, str):
return self.path == other
else:
return NotImplemented
def __hash__(self):
return hash(self.path)
def __ne__(self, other):
return not (self == other)
def __repr__(self):
return f'{self.__class__.__name__}({self.path!r})'
def __unicode__(self):
return self.path
def __contains__(self, label):
return label in self.path.split('.')
def __gt__(self, other):
return self.path > other.path
def __lt__(self, other):
return self.path < other.path
def __ge__(self, other):
return self.path >= other.path
def __le__(self, other):
return self.path <= other.path

View file

@ -0,0 +1,54 @@
from functools import total_ordering
from .. import i18n
from ..utils import str_coercible
@str_coercible
@total_ordering
class WeekDay:
NUM_WEEK_DAYS = 7
def __init__(self, index):
if not (0 <= index < self.NUM_WEEK_DAYS):
raise ValueError(
"index must be between 0 and %d" % self.NUM_WEEK_DAYS
)
self.index = index
def __eq__(self, other):
if isinstance(other, WeekDay):
return self.index == other.index
else:
return NotImplemented
def __hash__(self):
return hash(self.index)
def __lt__(self, other):
return self.position < other.position
def __repr__(self):
return f'{self.__class__.__name__}({self.index!r})'
def __unicode__(self):
return self.name
def get_name(self, width='wide', context='format'):
names = i18n.babel.dates.get_day_names(
width,
context,
i18n.get_locale()
)
return names[self.index]
@property
def name(self):
return self.get_name()
@property
def position(self):
return (
self.index -
i18n.get_locale().first_week_day
) % self.NUM_WEEK_DAYS

View file

@ -0,0 +1,57 @@
from ..utils import str_coercible
from .weekday import WeekDay
@str_coercible
class WeekDays:
def __init__(self, bit_string_or_week_days):
if isinstance(bit_string_or_week_days, str):
self._days = set()
if len(bit_string_or_week_days) != WeekDay.NUM_WEEK_DAYS:
raise ValueError(
'Bit string must be {} characters long.'.format(
WeekDay.NUM_WEEK_DAYS
)
)
for index, bit in enumerate(bit_string_or_week_days):
if bit not in '01':
raise ValueError(
'Bit string may only contain zeroes and ones.'
)
if bit == '1':
self._days.add(WeekDay(index))
elif isinstance(bit_string_or_week_days, WeekDays):
self._days = bit_string_or_week_days._days
else:
self._days = set(bit_string_or_week_days)
def __eq__(self, other):
if isinstance(other, WeekDays):
return self._days == other._days
elif isinstance(other, str):
return self.as_bit_string() == other
else:
return NotImplemented
def __iter__(self):
yield from sorted(self._days)
def __contains__(self, value):
return value in self._days
def __repr__(self):
return '{}({!r})'.format(
self.__class__.__name__,
self.as_bit_string()
)
def __unicode__(self):
return ', '.join(str(day) for day in self)
def as_bit_string(self):
return ''.join(
'1' if WeekDay(index) in self._days else '0'
for index in range(WeekDay.NUM_WEEK_DAYS)
)