12 files changed
+115
-97
No files matched your search
@@ -15,13 +15,13 @@ repos:
|
||||
args:
|
||||
- --isort
|
||||
- --flynt
|
||||
- --lint=flake8 --max-line-length=88
|
||||
- --lint=mypy --strict
|
||||
- --lint=pylint --max-line-length=88
|
||||
- --lint=flake8 --max-line-length=88 --ignore=E203,W503
|
||||
- --lint=mypy # --strict # commenting strict for now as we haven't maintained typing completely
|
||||
- --lint=pylint --max-line-length=88 --disable=W0511
|
||||
additional_dependencies:
|
||||
- black==23.3.0
|
||||
- flake8==5.0.4
|
||||
- flynt==0.78
|
||||
- flynt==0.77
|
||||
- isort==5.12.0
|
||||
- mypy==1.2.0
|
||||
- mypy==1.3.0
|
||||
- pylint==2.17.4
|
||||
@@ -1,4 +1,12 @@
|
||||
MONTHS_EN = (
|
||||
"""
|
||||
This files contains constants for the py-Nepali project.
|
||||
"""
|
||||
|
||||
# Nepali timezone
|
||||
NEPAL_TIMEZONE = "Asia/Kathmandu"
|
||||
|
||||
# Nepali months in english
|
||||
NEPALI_MONTHS_EN = (
|
||||
"Baishakh",
|
||||
"Jestha",
|
||||
"Ashad",
|
||||
@@ -13,7 +21,8 @@ MONTHS_EN = (
|
||||
"Chaitra",
|
||||
)
|
||||
|
||||
MONTHS_NE = (
|
||||
# Nepali months in Nepali (devanagari)
|
||||
NEPALI_MONTHS_NE = (
|
||||
"बैशाख",
|
||||
"जेठ",
|
||||
"असार",
|
||||
@@ -28,6 +37,7 @@ MONTHS_NE = (
|
||||
"चैत",
|
||||
)
|
||||
|
||||
# Week names in english
|
||||
WEEKS_EN = (
|
||||
"Sunday",
|
||||
"Monday",
|
||||
@@ -38,8 +48,10 @@ WEEKS_EN = (
|
||||
"Saturday",
|
||||
)
|
||||
|
||||
# Week abbreviation name in english
|
||||
WEEKS_ABBR_EN = ("Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat")
|
||||
|
||||
# Weeks names in Nepali (devanagari)
|
||||
WEEKS_NE = (
|
||||
"आइतबार",
|
||||
"सोमबार",
|
||||
@@ -50,4 +62,5 @@ WEEKS_NE = (
|
||||
"शनिबार",
|
||||
)
|
||||
|
||||
# Week abbreviation name in Nepali (devanagari)
|
||||
WEEKS_ABBR_NE = ("आइत", "सोम", "मंगल", "बुध", "बिही", "शुक्र", "शनि")
|
||||
@@ -17,31 +17,31 @@ class formatter_class_mixin:
|
||||
|
||||
@classmethod
|
||||
def init_formatter_class(cls):
|
||||
if not hasattr(cls, "__formatter_class__Cache"):
|
||||
if not hasattr(cls, "_formatter_class_cache"):
|
||||
from ._formatter import NepaliDateTimeFormatter
|
||||
|
||||
cls.__formatter_class__Cache = NepaliDateTimeFormatter
|
||||
return cls.__formatter_class__Cache
|
||||
cls._formatter_class_cache = NepaliDateTimeFormatter
|
||||
return cls._formatter_class_cache
|
||||
|
||||
@classmethod
|
||||
def get_strptime_method(cls):
|
||||
if not hasattr(cls, "_strptime_method_CACHE"):
|
||||
if not hasattr(cls, "_strptime_method_cache"):
|
||||
from .parser import strptime
|
||||
|
||||
cls._strptime_method_CACHE = strptime
|
||||
return cls._strptime_method_CACHE
|
||||
cls._strptime_method_cache = strptime
|
||||
return cls._strptime_method_cache
|
||||
|
||||
def strftime(self, format: str) -> str:
|
||||
return self.strftime_en(format)
|
||||
|
||||
def strftime_en(self, format: str) -> str:
|
||||
NepaliDateTimeFormatter = self.get_formatter_class()
|
||||
formatter = NepaliDateTimeFormatter(self, devanagari=False)
|
||||
nepali_datetime_formatter = self.get_formatter_class()
|
||||
formatter = nepali_datetime_formatter(self, devanagari=False)
|
||||
return formatter.get_str(format)
|
||||
|
||||
def strftime_ne(self, format: str) -> str:
|
||||
NepaliDateTimeFormatter = self.get_formatter_class()
|
||||
formatter = NepaliDateTimeFormatter(self, devanagari=True)
|
||||
nepali_datetime_formatter = self.get_formatter_class()
|
||||
formatter = nepali_datetime_formatter(self, devanagari=True)
|
||||
return formatter.get_str(format)
|
||||
|
||||
|
||||
@@ -194,12 +194,11 @@ class nepalidate(formatter_class_mixin):
|
||||
|
||||
@staticmethod
|
||||
def from_date(date_object):
|
||||
npDate = nepalidate(
|
||||
return nepalidate(
|
||||
*nepali_date_converter.english_to_nepali(
|
||||
date_object.year, date_object.month, date_object.day
|
||||
)
|
||||
)
|
||||
return npDate
|
||||
|
||||
@staticmethod
|
||||
def from_nepalidatetime(datetime_object):
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
import datetime as pythonDateTime
|
||||
|
||||
from nepali import number
|
||||
from nepali.timezone import now, to_nepali_timezone
|
||||
from nepali.exceptions import InvalidNepaliDateTimeObjectException
|
||||
from nepali.timezone import now
|
||||
|
||||
from ._datetime import nepalidate, nepalidatetime
|
||||
from .utils import to_nepalidatetime
|
||||
|
||||
|
||||
class HumanizeDateTime:
|
||||
@@ -29,20 +26,7 @@ class HumanizeDateTime:
|
||||
threshold (kwargs): threshold to be humanize
|
||||
format (kwargs): format to display behind threshold
|
||||
"""
|
||||
if type(datetime_obj) == nepalidatetime:
|
||||
self.datetime_obj = datetime_obj.to_datetime()
|
||||
elif type(datetime_obj) == nepalidate:
|
||||
self.datetime_obj = nepalidatetime.from_nepali_date(
|
||||
datetime_obj
|
||||
).to_datetime()
|
||||
elif type(datetime_obj) == pythonDateTime.date:
|
||||
self.datetime_obj = nepalidatetime.from_date(datetime_obj).to_datetime()
|
||||
elif type(datetime_obj) == pythonDateTime.datetime:
|
||||
self.datetime_obj = to_nepali_timezone(datetime_obj)
|
||||
else:
|
||||
raise InvalidNepaliDateTimeObjectException(
|
||||
"Argument must be instance of NepaliDate or NepaliDateTime or datetime.datetime or datetime.date"
|
||||
)
|
||||
self.datetime_obj = to_nepalidatetime(datetime_obj)
|
||||
|
||||
self.threshold = kwargs.get("threshold")
|
||||
self.format = kwargs.get("format")
|
||||
@@ -53,8 +37,11 @@ class HumanizeDateTime:
|
||||
def __calc_seconds(self):
|
||||
"""calculates total seconds from now"""
|
||||
current_date_time = now()
|
||||
date = self.datetime_obj
|
||||
self.seconds = int((current_date_time - date).total_seconds())
|
||||
|
||||
# TODO (@aj3sh): support datetime - nepalidatetime
|
||||
self.seconds = int(
|
||||
(current_date_time - self.datetime_obj.to_datetime()).total_seconds()
|
||||
)
|
||||
|
||||
self.interval_tense = self.__past_text
|
||||
if self.seconds < 0:
|
||||
@@ -67,13 +54,12 @@ class HumanizeDateTime:
|
||||
"""returns humanize string"""
|
||||
seconds = self.__calc_seconds() # calculating seconds
|
||||
|
||||
if self.threshold is not None:
|
||||
if seconds >= self.threshold:
|
||||
return self.get_datetime().strip()
|
||||
if self.threshold is not None and seconds >= self.threshold:
|
||||
return self._get_datetime_str().strip()
|
||||
|
||||
return self.get_humanize().strip()
|
||||
return self._get_humanize_str().strip()
|
||||
|
||||
def get_humanize(self):
|
||||
def _get_humanize_str(self):
|
||||
"""
|
||||
returns humanize datetime
|
||||
"""
|
||||
@@ -118,14 +104,13 @@ class HumanizeDateTime:
|
||||
str(interval_value) + " " + str(interval_text) + " " + self.interval_tense
|
||||
)
|
||||
|
||||
def get_datetime(self):
|
||||
def _get_datetime_str(self):
|
||||
"""
|
||||
returns date in nepali characters
|
||||
"""
|
||||
if not self.format:
|
||||
self.format = "%B %d, %Y"
|
||||
ndt = nepalidatetime.from_datetime(self.datetime_obj)
|
||||
return ndt.strftime_ne(self.format)
|
||||
return self.datetime_obj.strftime_ne(self.format)
|
||||
|
||||
def __str__(self):
|
||||
return self.to_str()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from functools import cached_property
|
||||
from typing import Any, Dict, Optional, Union
|
||||
|
||||
from .constants import MONTHS_EN, MONTHS_NE
|
||||
from nepali.constants import NEPALI_MONTHS_EN, NEPALI_MONTHS_NE
|
||||
|
||||
|
||||
class NepaliMonthMeta(type):
|
||||
@@ -40,7 +40,8 @@ class NepaliMonthMeta(type):
|
||||
|
||||
return cls._cache[value]
|
||||
|
||||
def _parse_str(cls, month: str) -> int:
|
||||
@staticmethod
|
||||
def _parse_str(month: str) -> int:
|
||||
"""
|
||||
Parses str value of the month and returns int.
|
||||
|
||||
@@ -54,7 +55,7 @@ class NepaliMonthMeta(type):
|
||||
return int(month)
|
||||
|
||||
month = month.capitalize()
|
||||
month_names = MONTHS_EN + MONTHS_NE
|
||||
month_names = NEPALI_MONTHS_EN + NEPALI_MONTHS_NE
|
||||
try:
|
||||
index = month_names.index(month)
|
||||
except ValueError:
|
||||
@@ -107,9 +108,9 @@ class nepalimonth(metaclass=NepaliMonthMeta):
|
||||
@cached_property
|
||||
def name(self) -> str:
|
||||
"""Month's english name"""
|
||||
return MONTHS_EN[self.__value - 1]
|
||||
return NEPALI_MONTHS_EN[self.__value - 1]
|
||||
|
||||
@cached_property
|
||||
def name_ne(self) -> str:
|
||||
"""Month's nepali name"""
|
||||
return MONTHS_NE[self.__value - 1]
|
||||
return NEPALI_MONTHS_NE[self.__value - 1]
|
||||
@@ -1,7 +1,7 @@
|
||||
from functools import cached_property
|
||||
from typing import Any, Dict, Optional, Union
|
||||
|
||||
from .constants import WEEKS_EN, WEEKS_NE, WEEKS_ABBR_EN, WEEKS_ABBR_NE
|
||||
from nepali.constants import WEEKS_ABBR_EN, WEEKS_ABBR_NE, WEEKS_EN, WEEKS_NE
|
||||
|
||||
|
||||
class NepaliWeekMeta(type):
|
||||
@@ -28,7 +28,7 @@ class NepaliWeekMeta(type):
|
||||
pass
|
||||
|
||||
# checking if week is valid
|
||||
if value is None or not (0 <= value <= 6):
|
||||
if value is None or not 0 <= value <= 6:
|
||||
raise ValueError(f"Invalid week: {week}")
|
||||
|
||||
# checking cache
|
||||
@@ -37,7 +37,8 @@ class NepaliWeekMeta(type):
|
||||
|
||||
return cls._cache[value]
|
||||
|
||||
def _parse_str(cls, week: str) -> int:
|
||||
@staticmethod
|
||||
def _parse_str(week: str) -> int:
|
||||
"""
|
||||
Parses str value of the week and returns int.
|
||||
|
||||
|
||||
@@ -65,10 +65,10 @@ def parse(datetime_str):
|
||||
eg. parse('2078-10-12') => <NepaliDateTime: 2078-10-12>
|
||||
"""
|
||||
standard_formats = _get_standard_formats()
|
||||
nepalidatetime_object = None
|
||||
for format in standard_formats:
|
||||
nepalidatetime_object = validate(datetime_str, format=format)
|
||||
if nepalidatetime_object is not None:
|
||||
return nepalidatetime_object
|
||||
try:
|
||||
return strptime(datetime_str, format=format)
|
||||
except FormatNotMatchException:
|
||||
pass
|
||||
|
||||
raise InvalidDateTimeFormatException("Invalid format to parse nepali datetime.")
|
||||
@@ -5,8 +5,8 @@ import re
|
||||
from typing import Optional, Tuple
|
||||
|
||||
from nepali.char import nepali_to_english_text
|
||||
from nepali.constants import NEPALI_MONTHS_EN, WEEKS_ABBR_EN, WEEKS_EN
|
||||
from nepali.datetime import nepalidatetime, nepalimonth, nepaliweek
|
||||
from nepali.datetime.constants import MONTHS_EN, WEEKS_ABBR_EN, WEEKS_EN
|
||||
|
||||
__nepali_time_re__CACHE = None
|
||||
|
||||
@@ -41,8 +41,8 @@ class NepaliTimeRE(dict):
|
||||
"z": r"(?P<z>[+-]\d\d:?[0-5]\d(:?[0-5]\d(\.\d{1,6})?)?|(?-i:Z))",
|
||||
"A": self.__seqToRE(WEEKS_EN, "A"),
|
||||
"a": self.__seqToRE(WEEKS_ABBR_EN, "a"),
|
||||
"B": self.__seqToRE(MONTHS_EN, "B"),
|
||||
"b": self.__seqToRE(MONTHS_EN, "b"),
|
||||
"B": self.__seqToRE(NEPALI_MONTHS_EN, "B"),
|
||||
"b": self.__seqToRE(NEPALI_MONTHS_EN, "b"),
|
||||
"p": self.__seqToRE(
|
||||
(
|
||||
"AM",
|
||||
@@ -71,36 +71,39 @@ class NepaliTimeRE(dict):
|
||||
regex = f"(?P<{directive}>{regex}"
|
||||
return f"{regex})"
|
||||
|
||||
def pattern(self, format):
|
||||
def pattern(self, date_format):
|
||||
"""
|
||||
Handle conversion from format directives to regex.
|
||||
"""
|
||||
processed_format = ""
|
||||
regex_chars = re.compile(r"([\\.^$*+?\(\){}\[\]|])")
|
||||
format = regex_chars.sub(r"\\\1", format)
|
||||
date_format = regex_chars.sub(r"\\\1", date_format)
|
||||
whitespace_replacement = re.compile(r"\s+")
|
||||
format = whitespace_replacement.sub(r"\\s+", format)
|
||||
while "%" in format:
|
||||
directive_index = format.index("%") + 1
|
||||
date_format = whitespace_replacement.sub(r"\\s+", date_format)
|
||||
while "%" in date_format:
|
||||
directive_index = date_format.index("%") + 1
|
||||
index_increment = 1
|
||||
|
||||
if format[directive_index] == "-":
|
||||
if date_format[directive_index] == "-":
|
||||
index_increment = 2
|
||||
|
||||
if format[directive_index : directive_index + index_increment] not in self:
|
||||
if (
|
||||
date_format[directive_index : directive_index + index_increment]
|
||||
not in self
|
||||
):
|
||||
return None
|
||||
|
||||
processed_format = "%s%s%s" % (
|
||||
processed_format,
|
||||
format[: directive_index - 1],
|
||||
self[format[directive_index : directive_index + index_increment]],
|
||||
date_format[: directive_index - 1],
|
||||
self[date_format[directive_index : directive_index + index_increment]],
|
||||
)
|
||||
format = format[directive_index + index_increment :]
|
||||
return f"^{processed_format}{format}$"
|
||||
date_format = date_format[directive_index + index_increment :]
|
||||
return f"^{processed_format}{date_format}$"
|
||||
|
||||
def compile(self, format):
|
||||
def compile(self, date_format):
|
||||
"""Return a compiled re object for the format string."""
|
||||
return re.compile(self.pattern(format), re.IGNORECASE)
|
||||
return re.compile(self.pattern(date_format), re.IGNORECASE)
|
||||
|
||||
|
||||
def get_nepali_time_re_object():
|
||||
@@ -131,7 +134,7 @@ def extract(datetime_str, format):
|
||||
# converting datetime_str to english if any exists
|
||||
datetime_str = nepali_to_english_text(datetime_str)
|
||||
|
||||
re_compiled_format = get_nepali_time_re_object().compile(format=format)
|
||||
re_compiled_format = get_nepali_time_re_object().compile(format)
|
||||
match = re_compiled_format.match(datetime_str)
|
||||
if match is None:
|
||||
return {}
|
||||
@@ -250,7 +253,7 @@ def __calculate_hour_minute_seconds(data: dict) -> Tuple[int, int, int, int]:
|
||||
s += "0" * (6 - len(s))
|
||||
fraction = int(s)
|
||||
|
||||
return (hour, minute, second, fraction)
|
||||
return hour, minute, second, fraction
|
||||
|
||||
|
||||
def __calculate_weekday(data: dict) -> Optional[nepaliweek]:
|
||||
@@ -320,7 +323,7 @@ def validate(datetime_str, format):
|
||||
"""
|
||||
|
||||
# 1. validate if format is correct.
|
||||
if get_nepali_time_re_object().pattern(format=format) is None:
|
||||
if get_nepali_time_re_object().pattern(format) is None:
|
||||
# regex pattern generation failed
|
||||
return None
|
||||
|
||||
|
||||
@@ -19,10 +19,23 @@ class nepalinumber:
|
||||
"""
|
||||
self.__value = self.__parse(value)
|
||||
|
||||
def _raise_parse_exception(self, obj, ex_class: Type[Exception] = ValueError):
|
||||
raise ex_class(
|
||||
def get_parse_exception(
|
||||
self, obj: object, ex_class: Type[Exception] = ValueError
|
||||
) -> Exception:
|
||||
"""
|
||||
Returns the exception object to be raised when the parse is failed.
|
||||
The methods also sets a proper message to the exception class.
|
||||
|
||||
:param obj: Object that is failed during the parse
|
||||
:type obj: object
|
||||
:param ex_class: Exception class type to be returned, defaults to ValueError
|
||||
:type ex_class: Type[Exception], optional
|
||||
:return: Exception object to be raised
|
||||
:rtype: Exception
|
||||
"""
|
||||
return ex_class(
|
||||
f"could not convert {obj.__class__.__name__} to {self.__class__.__name__}: '{obj}'"
|
||||
) from None
|
||||
)
|
||||
|
||||
def __parse(self, value: Any) -> Union[int, float]:
|
||||
"""
|
||||
@@ -61,10 +74,10 @@ class nepalinumber:
|
||||
|
||||
:raises ValueError: If the value is invalid
|
||||
"""
|
||||
result = 0
|
||||
result: float = 0
|
||||
sign = 1
|
||||
decimal_found = False
|
||||
decimal_place = 1
|
||||
decimal_place: float = 1
|
||||
i = 0
|
||||
|
||||
# for negative sign
|
||||
@@ -77,7 +90,7 @@ class nepalinumber:
|
||||
if value[i] == ".":
|
||||
if decimal_found:
|
||||
# decimal was already found
|
||||
self._raise_parse_exception(value)
|
||||
raise self.get_parse_exception(value) from None
|
||||
decimal_found = True
|
||||
i += 1
|
||||
continue
|
||||
@@ -86,7 +99,7 @@ class nepalinumber:
|
||||
if digit < 0 or digit > 9:
|
||||
# checking nepali character
|
||||
if value[i] not in NP_NUMBERS_SET:
|
||||
self._raise_parse_exception(value)
|
||||
raise self.get_parse_exception(value) from None
|
||||
digit = NP_NUMBERS.index(value[i])
|
||||
|
||||
if decimal_found:
|
||||
@@ -113,7 +126,7 @@ class nepalinumber:
|
||||
return self.__parse_str(str(obj))
|
||||
except (ValueError, TypeError):
|
||||
# object conversion must raise TypeError if fails
|
||||
self._raise_parse_exception(obj, ex_class=TypeError)
|
||||
raise self.get_parse_exception(obj, ex_class=TypeError) from None
|
||||
|
||||
def __convert_or_return(self, obj) -> Union["nepalinumber", object]:
|
||||
"""
|
||||
|
||||
@@ -3,9 +3,9 @@ from enum import Enum
|
||||
from typing import Union
|
||||
|
||||
|
||||
_mobile_number_re = re.compile(r"^(?:\+977|977)?(?:-)?(?:98|97|96)[0-9]{8}$")
|
||||
_mobile_number_re = re.compile(r"^(?:\+977|977)?(?:-)?(?:98|97|96)\d{8}$")
|
||||
_landline_number_re = re.compile(
|
||||
r"^(?:\+977|977)?(?:-)?(?:0)?(?:[01][1-9]|2[13-9]|[3-9][0-9])[0-9]{6,7}$"
|
||||
r"^(?:\+977|977)?(?:-)?(?:0)?(?:[01][1-9]|2[13-9]|[3-9]\d)\d{6,7}$"
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -7,13 +7,14 @@ import datetime
|
||||
import unittest
|
||||
from unittest.mock import patch
|
||||
|
||||
from nepali.constants import NEPAL_TIMEZONE
|
||||
from nepali.timezone import (
|
||||
NepaliTimeZone,
|
||||
get_timezone,
|
||||
now,
|
||||
to_nepali_timezone,
|
||||
to_utc_timezone,
|
||||
utc_now,
|
||||
now,
|
||||
get_timezone,
|
||||
)
|
||||
|
||||
|
||||
@@ -32,13 +33,13 @@ class TestNepaliTimeZone(unittest.TestCase):
|
||||
)
|
||||
|
||||
def test_nepali_timezone_tzname(self):
|
||||
self.assertEqual(self.nepali_timezone.tzname(None), "Asia/Kathmandu")
|
||||
self.assertEqual(self.nepali_timezone.tzname(None), NEPAL_TIMEZONE)
|
||||
|
||||
def test_nepali_timezone_str(self):
|
||||
self.assertEqual(str(self.nepali_timezone), "Asia/Kathmandu")
|
||||
self.assertEqual(str(self.nepali_timezone), NEPAL_TIMEZONE)
|
||||
|
||||
def test_nepali_timezone_repr(self):
|
||||
self.assertEqual(repr(self.nepali_timezone), "Asia/Kathmandu")
|
||||
self.assertEqual(repr(self.nepali_timezone), NEPAL_TIMEZONE)
|
||||
|
||||
def test_datetime_with_nepali_timezone(self):
|
||||
dt = datetime.datetime(2015, 10, 14, tzinfo=self.nepali_timezone)
|
||||
|
||||
+5
-3
@@ -1,6 +1,8 @@
|
||||
import datetime
|
||||
from typing import Union
|
||||
|
||||
from .constants import NEPAL_TIMEZONE
|
||||
|
||||
|
||||
class NepaliTimeZone(datetime.tzinfo):
|
||||
"""
|
||||
@@ -14,13 +16,13 @@ class NepaliTimeZone(datetime.tzinfo):
|
||||
return datetime.timedelta(0)
|
||||
|
||||
def tzname(self, dt):
|
||||
return "Asia/Kathmandu"
|
||||
return NEPAL_TIMEZONE
|
||||
|
||||
def __str__(self):
|
||||
return "Asia/Kathmandu"
|
||||
return NEPAL_TIMEZONE
|
||||
|
||||
def __repr__(self):
|
||||
return "Asia/Kathmandu"
|
||||
return NEPAL_TIMEZONE
|
||||
|
||||
def __eq__(self, o: object) -> bool:
|
||||
return isinstance(o, self.__class__)
|
||||
|
||||
Reference in new issue
Block a user