diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 01e87075..c8dd2596 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -29,7 +29,7 @@ jobs: runs-on: macos-latest strategy: matrix: - python-version: [2.7, 3.5, 3.6, 3.7, 3.8] + python-version: [3.6, 3.7, 3.8] steps: - uses: actions/checkout@v2 @@ -59,7 +59,7 @@ jobs: runs-on: windows-latest strategy: matrix: - python-version: [2.7, 3.5, 3.6, 3.7, 3.8] + python-version: [3.6, 3.7, 3.8] steps: - uses: actions/checkout@v2 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index b51b90a3..11f14865 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -26,139 +26,66 @@ jobs: run: | pip install pre-commit pre-commit run --all-files - Linux: + + Tests: needs: Linting - runs-on: ubuntu-latest + name: ${{ matrix.os }} / ${{ matrix.python-version }} + runs-on: ${{ matrix.os }}-latest strategy: matrix: - python-version: [2.7, 3.5, 3.6, 3.7, 3.8, pypy3] - + os: [Ubuntu, MacOS, Windows] + python-version: [3.6, 3.7, 3.8, pypy3] steps: - - uses: actions/checkout@v2 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v1 - with: - python-version: ${{ matrix.python-version }} - - name: Get full python version - id: full-python-version - run: | - echo ::set-output name=version::$(python -c "import sys; print('-'.join(str(v) for v in sys.version_info))") - - name: Install and set up Poetry - run: | - curl -fsS -o get-poetry.py https://raw.githubusercontent.com/sdispater/poetry/master/get-poetry.py - python get-poetry.py -y - source $HOME/.poetry/env - poetry config virtualenvs.in-project true - - name: Set up cache - uses: actions/cache@v1 - with: - path: .venv - key: venv-${{ runner.os }}-${{ steps.full-python-version.outputs.version }}-${{ hashFiles('**/poetry.lock') }} - - name: Upgrade pip - run: | - source $HOME/.poetry/env - poetry run python -m pip install pip -U - - name: Install dependencies - run: | - source $HOME/.poetry/env - poetry install -vvv - - name: Test Pure Python - run: | - source $HOME/.poetry/env - PENDULUM_EXTENSIONS=0 poetry run pytest -q tests - - name: Test - run: | - source $HOME/.poetry/env - poetry run pytest -q tests - poetry install + - uses: actions/checkout@v2 - MacOS: - needs: Linting - runs-on: macos-latest - strategy: - matrix: - python-version: [2.7, 3.5, 3.6, 3.7, 3.8, pypy3] + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v1 + with: + python-version: ${{ matrix.python-version }} - steps: - - uses: actions/checkout@v2 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v1 - with: - python-version: ${{ matrix.python-version }} - - name: Get full python version - id: full-python-version - run: | - echo ::set-output name=version::$(python -c "import sys; print('-'.join(str(v) for v in sys.version_info))") - - name: Install and set up Poetry - run: | - curl -fsS -o get-poetry.py https://raw.githubusercontent.com/sdispater/poetry/master/get-poetry.py - python get-poetry.py -y - source $HOME/.poetry/env - poetry config virtualenvs.in-project true - - name: Set up cache - uses: actions/cache@v1 - with: - path: .venv - key: venv-${{ runner.os }}-${{ steps.full-python-version.outputs.version }}-fix-${{ hashFiles('**/poetry.lock') }} - - name: Upgrade pip - run: | - source $HOME/.poetry/env - poetry run python -m pip install pip -U - - name: Install dependencies - run: | - source $HOME/.poetry/env - poetry install -vvv - - name: Test Pure Python - run: | - source $HOME/.poetry/env - PENDULUM_EXTENSIONS=0 poetry run pytest -q tests - - name: Test - run: | - source $HOME/.poetry/env - poetry run pytest -q tests - Windows: - needs: Linting - runs-on: windows-latest - strategy: - matrix: - python-version: [2.7, 3.5, 3.6, 3.7, 3.8] + - name: Get full Python version + id: full-python-version + shell: bash + run: echo ::set-output name=version::$(python -c "import sys; print('-'.join(str(v) for v in sys.version_info))") - steps: - - uses: actions/checkout@v2 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v1 - with: - python-version: ${{ matrix.python-version }} - - name: Get full python version - id: full-python-version - shell: bash - run: | - echo ::set-output name=version::$(python -c "import sys; print('-'.join(str(v) for v in sys.version_info))") - - name: Install and setup Poetry - run: | - Invoke-WebRequest https://raw.githubusercontent.com/sdispater/poetry/master/get-poetry.py -O get-poetry.py - python get-poetry.py -y - $env:Path += ";$env:Userprofile\.poetry\bin" - poetry config virtualenvs.in-project true - - name: Set up cache - uses: actions/cache@v1 - with: - path: .venv - key: venv-${{ runner.os }}-${{ steps.full-python-version.outputs.version }}-${{ hashFiles('**/poetry.lock') }} - - name: Upgrade pip - run: | - $env:Path += ";$env:Userprofile\.poetry\bin" - poetry run python -m pip install pip -U - - name: Install dependencies - run: | - $env:Path += ";$env:Userprofile\.poetry\bin" - poetry install -vvv - - name: Test Pure Python - run: | - $env:Path += ";$env:Userprofile\.poetry\bin" - $env:PENDULUM_EXTENSIONS = "0" - poetry run pytest -q tests - - name: Test - run: | - $env:Path += ";$env:Userprofile\.poetry\bin" - poetry run pytest -q tests + - name: Install poetry + shell: bash + run: | + curl -fsS -o get-poetry.py https://raw.githubusercontent.com/sdispater/poetry/master/get-poetry.py + python get-poetry.py --preview -y + echo "::set-env name=PATH::$HOME/.poetry/bin:$PATH" + + - name: Configure poetry + shell: bash + run: poetry config virtualenvs.in-project true + + - name: Set up cache + uses: actions/cache@v2 + id: cache + with: + path: .venv + key: venv-${{ runner.os }}-${{ steps.full-python-version.outputs.version }}-${{ hashFiles('**/poetry.lock') }} + + - name: Ensure cache is healthy + if: steps.cache.outputs.cache-hit == 'true' + shell: bash + run: poetry run pip --version >/dev/null 2>&1 || rm -rf .venv + + - name: Upgrade pip + shell: bash + run: | + poetry run python -m pip install pip -U + + - name: Install dependencies + shell: bash + run: poetry install -vvv + + - name: Test Pure Python + shell: bash + run: | + PENDULUM_EXTENSIONS=0 poetry run pytest -q tests + + - name: Test + shell: bash + run: | + poetry run pytest -q tests diff --git a/build-wheels.sh b/build-wheels.sh index af63d1b1..1c8f1cb5 100755 --- a/build-wheels.sh +++ b/build-wheels.sh @@ -13,6 +13,9 @@ for PYBIN in /opt/python/cp3*/bin; do if [ "$PYBIN" == "/opt/python/cp34-cp34m/bin" ]; then continue fi + if [ "$PYBIN" == "/opt/python/cp35-cp35m/bin" ]; then + continue + fi rm -rf build "${PYBIN}/python" $HOME/.poetry/bin/poetry build -vvv done diff --git a/pendulum/__init__.py b/pendulum/__init__.py index b19f87d5..275ea4e6 100644 --- a/pendulum/__init__.py +++ b/pendulum/__init__.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - import datetime as _datetime from typing import Optional @@ -50,7 +48,6 @@ from .tz import timezone from .tz import timezones from .tz.timezone import Timezone as _Timezone -from .utils._compat import _HAS_FOLD _TEST_NOW = None # type: Optional[DateTime] @@ -61,8 +58,9 @@ _formatter = Formatter() -def _safe_timezone(obj): - # type: (Optional[Union[str, float, _datetime.tzinfo, _Timezone]]) -> _Timezone +def _safe_timezone( + obj: Optional[Union[str, float, _datetime.tzinfo, _Timezone]] +) -> _Timezone: """ Creates a timezone instance from a string, Timezone, TimezoneInfo or integer offset. @@ -94,26 +92,24 @@ def _safe_timezone(obj): # Public API def datetime( - year, # type: int - month, # type: int - day, # type: int - hour=0, # type: int - minute=0, # type: int - second=0, # type: int - microsecond=0, # type: int - tz=UTC, # type: Optional[Union[str, float, _Timezone]] - dst_rule=POST_TRANSITION, # type: str -): # type: (...) -> DateTime + year: int, + month: int, + day: int, + hour: int = 0, + minute: int = 0, + second: int = 0, + microsecond: int = 0, + tz: Optional[Union[str, float, _Timezone]] = UTC, + dst_rule: str = POST_TRANSITION, +) -> DateTime: """ Creates a new DateTime instance from a specific date and time. """ if tz is not None: tz = _safe_timezone(tz) - if not _HAS_FOLD: - dt = naive(year, month, day, hour, minute, second, microsecond) - else: - dt = _datetime.datetime(year, month, day, hour, minute, second, microsecond) + dt = _datetime.datetime(year, month, day, hour, minute, second, microsecond) + if tz is not None: dt = tz.convert(dt, dst_rule=dst_rule) @@ -131,8 +127,14 @@ def datetime( def local( - year, month, day, hour=0, minute=0, second=0, microsecond=0 -): # type: (int, int, int, int, int, int, int) -> DateTime + year: int, + month: int, + day: int, + hour: int = 0, + minute: int = 0, + second: int = 0, + microsecond: int = 0, +) -> DateTime: """ Return a DateTime in the local timezone. """ @@ -142,22 +144,28 @@ def local( def naive( - year, month, day, hour=0, minute=0, second=0, microsecond=0 -): # type: (int, int, int, int, int, int, int) -> DateTime + year: int, + month: int, + day: int, + hour: int = 0, + minute: int = 0, + second: int = 0, + microsecond: int = 0, +) -> DateTime: """ Return a naive DateTime. """ return DateTime(year, month, day, hour, minute, second, microsecond) -def date(year, month, day): # type: (int, int, int) -> Date +def date(year: int, month: int, day: int) -> Date: """ Create a new Date instance. """ return Date(year, month, day) -def time(hour, minute=0, second=0, microsecond=0): # type: (int, int, int, int) -> Time +def time(hour: int, minute: int = 0, second: int = 0, microsecond: int = 0) -> Time: """ Create a new Time instance. """ @@ -165,8 +173,8 @@ def time(hour, minute=0, second=0, microsecond=0): # type: (int, int, int, int) def instance( - dt, tz=UTC -): # type: (_datetime.datetime, Optional[Union[str, _Timezone]]) -> DateTime + dt: _datetime.datetime, tz: Optional[Union[str, _Timezone]] = UTC +) -> DateTime: """ Create a DateTime instance from a datetime one. """ @@ -194,7 +202,7 @@ def instance( ) -def now(tz=None): # type: (Optional[Union[str, _Timezone]]) -> DateTime +def now(tz: Optional[Union[str, _Timezone]] = None) -> DateTime: """ Get a DateTime instance for the current date and time. """ @@ -225,25 +233,25 @@ def now(tz=None): # type: (Optional[Union[str, _Timezone]]) -> DateTime dt.second, dt.microsecond, tzinfo=dt.tzinfo, - fold=dt.fold if _HAS_FOLD else 0, + fold=dt.fold, ) -def today(tz="local"): # type: (Union[str, _Timezone]) -> DateTime +def today(tz: Union[str, _Timezone] = "local") -> DateTime: """ Create a DateTime instance for today. """ return now(tz).start_of("day") -def tomorrow(tz="local"): # type: (Union[str, _Timezone]) -> DateTime +def tomorrow(tz: Union[str, _Timezone] = "local") -> DateTime: """ Create a DateTime instance for today. """ return today(tz).add(days=1) -def yesterday(tz="local"): # type: (Union[str, _Timezone]) -> DateTime +def yesterday(tz: Union[str, _Timezone] = "local") -> DateTime: """ Create a DateTime instance for today. """ @@ -251,11 +259,11 @@ def yesterday(tz="local"): # type: (Union[str, _Timezone]) -> DateTime def from_format( - string, - fmt, - tz=UTC, - locale=None, # noqa -): # type: (str, str, Union[str, _Timezone], Optional[str]) -> DateTime + string: str, + fmt: str, + tz: Union[str, _Timezone] = UTC, + locale: Optional[str] = None, # noqa +) -> DateTime: """ Creates a DateTime instance from a specific format. """ @@ -267,8 +275,8 @@ def from_format( def from_timestamp( - timestamp, tz=UTC -): # type: (Union[int, float], Union[str, _Timezone]) -> DateTime + timestamp: Union[int, float], tz: Union[str, _Timezone] = UTC +) -> DateTime: """ Create a DateTime instance from a timestamp. """ @@ -285,16 +293,16 @@ def from_timestamp( def duration( - days=0, # type: float - seconds=0, # type: float - microseconds=0, # type: float - milliseconds=0, # type: float - minutes=0, # type: float - hours=0, # type: float - weeks=0, # type: float - years=0, # type: float - months=0, # type: float -): # type: (...) -> Duration + days: float = 0, + seconds: float = 0, + microseconds: float = 0, + milliseconds: float = 0, + minutes: float = 0, + hours: float = 0, + weeks: float = 0, + years: float = 0, + months: float = 0, +) -> Duration: """ Create a Duration instance. """ @@ -311,7 +319,7 @@ def duration( ) -def period(start, end, absolute=False): # type: (DateTime, DateTime, bool) -> Period +def period(start: DateTime, end: DateTime, absolute: bool = False) -> Period: """ Create a Period instance. """ diff --git a/pendulum/datetime.py b/pendulum/datetime.py index feb140fe..d277446b 100644 --- a/pendulum/datetime.py +++ b/pendulum/datetime.py @@ -5,8 +5,11 @@ import calendar import datetime +from typing import Callable +from typing import Dict +from typing import List from typing import Optional -from typing import TypeVar +from typing import Tuple from typing import Union import pendulum @@ -31,24 +34,19 @@ from .date import Date from .exceptions import PendulumException from .helpers import add_duration -from .helpers import timestamp from .period import Period from .time import Time from .tz import UTC from .tz.timezone import Timezone -from .utils._compat import _HAS_FOLD - - -_D = TypeVar("_D", bound="DateTime") class DateTime(datetime.datetime, Date): - EPOCH = None # type: DateTime + EPOCH: Optional["DateTime"] = None # Formats - _FORMATS = { + _FORMATS: Dict[str, Union[str, Callable]] = { "atom": ATOM, "cookie": COOKIE, "iso8601": lambda dt: dt.isoformat(), @@ -62,9 +60,9 @@ class DateTime(datetime.datetime, Date): "w3c": W3C, } - _EPOCH = datetime.datetime(1970, 1, 1, tzinfo=UTC) + _EPOCH: datetime.datetime = datetime.datetime(1970, 1, 1, tzinfo=UTC) - _MODIFIERS_VALID_UNITS = [ + _MODIFIERS_VALID_UNITS: List[str] = [ "second", "minute", "hour", @@ -76,48 +74,26 @@ class DateTime(datetime.datetime, Date): "century", ] - if not _HAS_FOLD: - - def __new__( - cls, - year, - month, - day, - hour=0, - minute=0, - second=0, - microsecond=0, - tzinfo=None, - fold=0, - ): - self = datetime.datetime.__new__( - cls, year, month, day, hour, minute, second, microsecond, tzinfo=tzinfo - ) - - self._fold = fold - - return self - @classmethod - def now(cls, tz=None): # type: (Optional[Union[str, Timezone]]) -> DateTime + def now(cls, tz: Optional[Union[str, Timezone]] = None) -> "DateTime": """ Get a DateTime instance for the current date and time. """ return pendulum.now(tz) @classmethod - def utcnow(cls): # type: () -> DateTime + def utcnow(cls) -> "DateTime": """ Get a DateTime instance for the current date and time in UTC. """ return pendulum.now(UTC) @classmethod - def today(cls): # type: () -> DateTime + def today(cls) -> "DateTime": return pendulum.now() @classmethod - def strptime(cls, time, fmt): # type: (str, str) -> DateTime + def strptime(cls, time: str, fmt: str) -> "DateTime": return pendulum.instance(datetime.datetime.strptime(time, fmt)) # Getters/Setters @@ -154,48 +130,14 @@ def set( year, month, day, hour, minute, second, microsecond, tz=tz ) - if not _HAS_FOLD: - - @property - def fold(self): - return self._fold - - def timestamp(self): - if self.tzinfo is None: - s = timestamp(self) - - return s + self.microsecond / 1e6 - else: - kwargs = {"tzinfo": self.tzinfo} - - if _HAS_FOLD: - kwargs["fold"] = self.fold - - dt = datetime.datetime( - self.year, - self.month, - self.day, - self.hour, - self.minute, - self.second, - self.microsecond, - **kwargs - ) - return (dt - self._EPOCH).total_seconds() - @property - def float_timestamp(self): + def float_timestamp(self) -> float: return self.timestamp() @property - def int_timestamp(self): + def int_timestamp(self) -> int: # Workaround needed to avoid inaccuracy # for far into the future datetimes - kwargs = {"tzinfo": self.tzinfo} - - if _HAS_FOLD: - kwargs["fold"] = self.fold - dt = datetime.datetime( self.year, self.month, @@ -204,7 +146,8 @@ def int_timestamp(self): self.minute, self.second, self.microsecond, - **kwargs + tzinfo=self.tzinfo, + fold=self.fold, ) delta = dt - self._EPOCH @@ -212,26 +155,26 @@ def int_timestamp(self): return delta.days * SECONDS_PER_DAY + delta.seconds @property - def offset(self): + def offset(self) -> int: return self.get_offset() @property - def offset_hours(self): + def offset_hours(self) -> int: return self.get_offset() / SECONDS_PER_MINUTE / MINUTES_PER_HOUR @property - def timezone(self): # type: () -> Optional[Timezone] + def timezone(self) -> Optional[Timezone]: if not isinstance(self.tzinfo, Timezone): return return self.tzinfo @property - def tz(self): # type: () -> Optional[Timezone] + def tz(self) -> Optional[Timezone]: return self.timezone @property - def timezone_name(self): # type: () -> Optional[str] + def timezone_name(self) -> str: tz = self.timezone if tz is None: @@ -240,28 +183,28 @@ def timezone_name(self): # type: () -> Optional[str] return tz.name @property - def age(self): + def age(self) -> int: return self.date().diff(self.now(self.tz).date(), abs=False).in_years() - def is_local(self): + def is_local(self) -> bool: return self.offset == self.in_timezone(pendulum.local_timezone()).offset - def is_utc(self): + def is_utc(self) -> bool: return self.offset == UTC.offset - def is_dst(self): + def is_dst(self) -> bool: return self.dst() != datetime.timedelta() - def get_offset(self): + def get_offset(self) -> int: return int(self.utcoffset().total_seconds()) - def date(self): + def date(self) -> Date: return Date(self.year, self.month, self.day) - def time(self): + def time(self) -> Time: return Time(self.hour, self.minute, self.second, self.microsecond) - def naive(self): # type: (_D) -> _D + def naive(self) -> "DateTime": """ Return the DateTime without timezone information. """ @@ -275,46 +218,23 @@ def naive(self): # type: (_D) -> _D self.microsecond, ) - def on(self, year, month, day): + def on(self, year: int, month: int, day: int) -> "DateTime": """ Returns a new instance with the current date set to a different date. - - :param year: The year - :type year: int - - :param month: The month - :type month: int - - :param day: The day - :type day: int - - :rtype: DateTime """ return self.set(year=int(year), month=int(month), day=int(day)) - def at(self, hour, minute=0, second=0, microsecond=0): + def at( + self, hour: int, minute: int = 0, second: int = 0, microsecond: int = 0 + ) -> "DateTime": """ Returns a new instance with the current time to a different time. - - :param hour: The hour - :type hour: int - - :param minute: The minute - :type minute: int - - :param second: The second - :type second: int - - :param microsecond: The microsecond - :type microsecond: int - - :rtype: DateTime """ return self.set( hour=hour, minute=minute, second=second, microsecond=microsecond ) - def in_timezone(self, tz): # type: (Union[str, Timezone]) -> DateTime + def in_timezone(self, tz: Union[str, Timezone]) -> "DateTime": """ Set the instance's timezone from a string or object. """ @@ -322,7 +242,7 @@ def in_timezone(self, tz): # type: (Union[str, Timezone]) -> DateTime return tz.convert(self, dst_rule=pendulum.POST_TRANSITION) - def in_tz(self, tz): # type: (Union[str, Timezone]) -> DateTime + def in_tz(self, tz: Union[str, Timezone]) -> "DateTime": """ Set the instance's timezone from a string or object. """ @@ -330,51 +250,39 @@ def in_tz(self, tz): # type: (Union[str, Timezone]) -> DateTime # STRING FORMATTING - def to_time_string(self): + def to_time_string(self) -> str: """ Format the instance as time. - - :rtype: str """ return self.format("HH:mm:ss") - def to_datetime_string(self): + def to_datetime_string(self) -> str: """ Format the instance as date and time. - - :rtype: str """ return self.format("YYYY-MM-DD HH:mm:ss") - def to_day_datetime_string(self): + def to_day_datetime_string(self) -> str: """ Format the instance as day, date and time (in english). - - :rtype: str """ return self.format("ddd, MMM D, YYYY h:mm A", locale="en") - def to_atom_string(self): + def to_atom_string(self) -> str: """ Format the instance as ATOM. - - :rtype: str """ return self._to_string("atom") - def to_cookie_string(self): + def to_cookie_string(self) -> str: """ Format the instance as COOKIE. - - :rtype: str """ return self._to_string("cookie", locale="en") - def to_iso8601_string(self): + def to_iso8601_string(self) -> str: """ Format the instance as ISO 8601. - - :rtype: str """ string = self._to_string("iso8601") @@ -383,81 +291,57 @@ def to_iso8601_string(self): return string - def to_rfc822_string(self): + def to_rfc822_string(self) -> str: """ Format the instance as RFC 822. - - :rtype: str """ return self._to_string("rfc822") - def to_rfc850_string(self): + def to_rfc850_string(self) -> str: """ Format the instance as RFC 850. - - :rtype: str """ return self._to_string("rfc850") - def to_rfc1036_string(self): + def to_rfc1036_string(self) -> str: """ Format the instance as RFC 1036. - - :rtype: str """ return self._to_string("rfc1036") - def to_rfc1123_string(self): + def to_rfc1123_string(self) -> str: """ Format the instance as RFC 1123. - - :rtype: str """ return self._to_string("rfc1123") - def to_rfc2822_string(self): + def to_rfc2822_string(self) -> str: """ Format the instance as RFC 2822. - - :rtype: str """ return self._to_string("rfc2822") - def to_rfc3339_string(self): + def to_rfc3339_string(self) -> str: """ Format the instance as RFC 3339. - - :rtype: str """ return self._to_string("rfc3339") - def to_rss_string(self): + def to_rss_string(self) -> str: """ Format the instance as RSS. - - :rtype: str """ return self._to_string("rss") - def to_w3c_string(self): + def to_w3c_string(self) -> str: """ Format the instance as W3C. - - :rtype: str """ return self._to_string("w3c") - def _to_string(self, fmt, locale=None): + def _to_string(self, fmt: str, locale: Optional[str] = None) -> str: """ Format the instance to a common string format. - - :param fmt: The name of the string format - :type fmt: string - - :param locale: The locale to use - :type locale: str or None - - :rtype: str """ if fmt not in self._FORMATS: raise ValueError("Format [{}] is not supported".format(fmt)) @@ -468,10 +352,10 @@ def _to_string(self, fmt, locale=None): return self.format(fmt, locale=locale) - def __str__(self): + def __str__(self) -> str: return self.isoformat("T") - def __repr__(self): + def __repr__(self) -> str: us = "" if self.microsecond: us = ", {}".format(self.microsecond) @@ -496,15 +380,11 @@ def __repr__(self): ) # Comparisons - def closest(self, dt1, dt2, *dts): + def closest( + self, dt1: datetime.datetime, dt2: datetime.datetime, *dts: datetime.datetime + ) -> "DateTime": """ Get the farthest date from the instance. - - :type dt1: datetime.datetime - :type dt2: datetime.datetime - :type dts: list[datetime.datetime,] - - :rtype: DateTime """ dt1 = pendulum.instance(dt1) dt2 = pendulum.instance(dt2) @@ -513,15 +393,11 @@ def closest(self, dt1, dt2, *dts): return min(dts)[1] - def farthest(self, dt1, dt2, *dts): + def farthest( + self, dt1: datetime.datetime, dt2: datetime.datetime, *dts: datetime.datetime + ) -> "DateTime": """ Get the farthest date from the instance. - - :type dt1: datetime.datetime - :type dt2: datetime.datetime - :type dts: list[datetime.datetime,] - - :rtype: DateTime """ dt1 = pendulum.instance(dt1) dt2 = pendulum.instance(dt2) @@ -531,54 +407,42 @@ def farthest(self, dt1, dt2, *dts): return max(dts)[1] - def is_future(self): + def is_future(self) -> bool: """ Determines if the instance is in the future, ie. greater than now. - - :rtype: bool """ return self > self.now(self.timezone) - def is_past(self): + def is_past(self) -> bool: """ Determines if the instance is in the past, ie. less than now. - - :rtype: bool """ return self < self.now(self.timezone) - def is_long_year(self): + def is_long_year(self) -> bool: """ Determines if the instance is a long year See link `https://en.wikipedia.org/wiki/ISO_8601#Week_dates`_ - - :rtype: bool """ return ( pendulum.datetime(self.year, 12, 28, 0, 0, 0, tz=self.tz).isocalendar()[1] == 53 ) - def is_same_day(self, dt): + def is_same_day(self, dt: datetime.datetime) -> bool: """ Checks if the passed in date is the same day as the instance current day. - - :type dt: DateTime or datetime or str or int - - :rtype: bool """ dt = pendulum.instance(dt) return self.to_date_string() == dt.to_date_string() - def is_anniversary(self, dt=None): + def is_anniversary(self, dt: Optional[datetime.datetime] = None) -> bool: """ Check if its the anniversary. Compares the date/month values of the two dates. - - :rtype: bool """ if dt is None: dt = self.now(self.tz) @@ -587,24 +451,19 @@ def is_anniversary(self, dt=None): return (self.month, self.day) == (instance.month, instance.day) - # the additional method for checking if today is the anniversary day - # the alias is provided to start using a new name and keep the backward compatibility - # the old name can be completely replaced with the new in one of the future versions - is_birthday = is_anniversary - # ADDITIONS AND SUBSTRACTIONS def add( self, - years=0, - months=0, - weeks=0, - days=0, - hours=0, - minutes=0, - seconds=0, - microseconds=0, - ): # type: (_D, int, int, int, int, int, int, int, int) -> _D + years: int = 0, + months: int = 0, + weeks: int = 0, + days: int = 0, + hours: int = 0, + minutes: int = 0, + seconds: int = 0, + microseconds: int = 0, + ) -> "DateTime": """ Add a duration to the instance. @@ -680,43 +539,17 @@ def add( def subtract( self, - years=0, - months=0, - weeks=0, - days=0, - hours=0, - minutes=0, - seconds=0, - microseconds=0, - ): + years: int = 0, + months: int = 0, + weeks: int = 0, + days: int = 0, + hours: int = 0, + minutes: int = 0, + seconds: int = 0, + microseconds: int = 0, + ) -> "DateTime": """ Remove duration from the instance. - - :param years: The number of years - :type years: int - - :param months: The number of months - :type months: int - - :param weeks: The number of weeks - :type weeks: int - - :param days: The number of days - :type days: int - - :param hours: The number of hours - :type hours: int - - :param minutes: The number of minutes - :type minutes: int - - :param seconds: The number of seconds - :type seconds: int - - :param microseconds: The number of microseconds - :type microseconds: int - - :rtype: DateTime """ return self.add( years=-years, @@ -732,14 +565,9 @@ def subtract( # Adding a final underscore to the method name # to avoid errors for PyPy which already defines # a _add_timedelta method - def _add_timedelta_(self, delta): + def _add_timedelta_(self, delta: datetime.timedelta) -> "DateTime": """ Add timedelta duration to the instance. - - :param delta: The timedelta instance - :type delta: pendulum.Duration or datetime.timedelta - - :rtype: DateTime """ if isinstance(delta, pendulum.Period): return self.add( @@ -759,14 +587,9 @@ def _add_timedelta_(self, delta): return self.add(seconds=delta.total_seconds()) - def _subtract_timedelta(self, delta): + def _subtract_timedelta(self, delta: datetime.timedelta) -> "DateTime": """ Remove timedelta duration from the instance. - - :param delta: The timedelta instance - :type delta: pendulum.Duration or datetime.timedelta - - :rtype: DateTime """ if isinstance(delta, pendulum.Duration): return self.subtract( @@ -777,16 +600,9 @@ def _subtract_timedelta(self, delta): # DIFFERENCES - def diff(self, dt=None, abs=True): + def diff(self, dt: Optional["DateTime"] = None, abs: bool = True) -> Period: """ - Returns the difference between two DateTime objects represented as a Duration. - - :type dt: DateTime or None - - :param abs: Whether to return an absolute interval or not - :type abs: bool - - :rtype: Period + Returns the difference between two DateTime objects represented as a Period. """ if dt is None: dt = self.now(self.tz) @@ -795,10 +611,10 @@ def diff(self, dt=None, abs=True): def diff_for_humans( self, - other=None, # type: Optional[DateTime] - absolute=False, # type: bool - locale=None, # type: Optional[str] - ): # type: (...) -> str + other: Optional["DateTime"] = None, + absolute: bool = False, + locale: Optional[str] = None, + ) -> str: """ Get the difference in a human readable format in the current locale. @@ -828,7 +644,7 @@ def diff_for_humans( return pendulum.format_diff(diff, is_now, absolute, locale) # Modifiers - def start_of(self, unit): + def start_of(self, unit: str) -> "DateTime": """ Returns a copy of the instance with the time reset with the following rules: @@ -842,18 +658,13 @@ def start_of(self, unit): * year: date to first day of the year and time to 00:00:00 * decade: date to first day of the decade and time to 00:00:00 * century: date to first day of century and time to 00:00:00 - - :param unit: The unit to reset to - :type unit: str - - :rtype: DateTime """ if unit not in self._MODIFIERS_VALID_UNITS: raise ValueError('Invalid unit "{}" for start_of()'.format(unit)) return getattr(self, "_start_of_{}".format(unit))() - def end_of(self, unit): + def end_of(self, unit: str) -> "DateTime": """ Returns a copy of the instance with the time reset with the following rules: @@ -867,164 +678,125 @@ def end_of(self, unit): * year: date to last day of the year and time to 23:59:59.999999 * decade: date to last day of the decade and time to 23:59:59.999999 * century: date to last day of century and time to 23:59:59.999999 - - :param unit: The unit to reset to - :type unit: str - - :rtype: DateTime """ if unit not in self._MODIFIERS_VALID_UNITS: raise ValueError('Invalid unit "%s" for end_of()' % unit) return getattr(self, "_end_of_%s" % unit)() - def _start_of_second(self): + def _start_of_second(self) -> "DateTime": """ Reset microseconds to 0. - - :rtype: DateTime """ return self.set(microsecond=0) - def _end_of_second(self): + def _end_of_second(self) -> "DateTime": """ Set microseconds to 999999. - - :rtype: DateTime """ return self.set(microsecond=999999) - def _start_of_minute(self): + def _start_of_minute(self) -> "DateTime": """ Reset seconds and microseconds to 0. - - :rtype: DateTime """ return self.set(second=0, microsecond=0) - def _end_of_minute(self): + def _end_of_minute(self) -> "DateTime": """ Set seconds to 59 and microseconds to 999999. - - :rtype: DateTime """ return self.set(second=59, microsecond=999999) - def _start_of_hour(self): + def _start_of_hour(self) -> "DateTime": """ Reset minutes, seconds and microseconds to 0. - - :rtype: DateTime """ return self.set(minute=0, second=0, microsecond=0) - def _end_of_hour(self): + def _end_of_hour(self) -> "DateTime": """ Set minutes and seconds to 59 and microseconds to 999999. - - :rtype: DateTime """ return self.set(minute=59, second=59, microsecond=999999) - def _start_of_day(self): + def _start_of_day(self) -> "DateTime": """ - Reset the time to 00:00:00 - - :rtype: DateTime + Reset the time to 00:00:00. """ return self.at(0, 0, 0, 0) - def _end_of_day(self): + def _end_of_day(self) -> "DateTime": """ - Reset the time to 23:59:59.999999 - - :rtype: DateTime + Reset the time to 23:59:59.999999. """ return self.at(23, 59, 59, 999999) - def _start_of_month(self): + def _start_of_month(self) -> "DateTime": """ Reset the date to the first day of the month and the time to 00:00:00. - - :rtype: DateTime """ return self.set(self.year, self.month, 1, 0, 0, 0, 0) - def _end_of_month(self): + def _end_of_month(self) -> "DateTime": """ Reset the date to the last day of the month and the time to 23:59:59.999999. - - :rtype: DateTime """ return self.set(self.year, self.month, self.days_in_month, 23, 59, 59, 999999) - def _start_of_year(self): + def _start_of_year(self) -> "DateTime": """ Reset the date to the first day of the year and the time to 00:00:00. - - :rtype: DateTime """ return self.set(self.year, 1, 1, 0, 0, 0, 0) - def _end_of_year(self): + def _end_of_year(self) -> "DateTime": """ Reset the date to the last day of the year - and the time to 23:59:59.999999 - - :rtype: DateTime + and the time to 23:59:59.999999. """ return self.set(self.year, 12, 31, 23, 59, 59, 999999) - def _start_of_decade(self): + def _start_of_decade(self) -> "DateTime": """ Reset the date to the first day of the decade and the time to 00:00:00. - - :rtype: DateTime """ year = self.year - self.year % YEARS_PER_DECADE return self.set(year, 1, 1, 0, 0, 0, 0) - def _end_of_decade(self): + def _end_of_decade(self) -> "DateTime": """ Reset the date to the last day of the decade and the time to 23:59:59.999999. - - :rtype: DateTime """ year = self.year - self.year % YEARS_PER_DECADE + YEARS_PER_DECADE - 1 return self.set(year, 12, 31, 23, 59, 59, 999999) - def _start_of_century(self): + def _start_of_century(self) -> "DateTime": """ Reset the date to the first day of the century and the time to 00:00:00. - - :rtype: DateTime """ year = self.year - 1 - (self.year - 1) % YEARS_PER_CENTURY + 1 return self.set(year, 1, 1, 0, 0, 0, 0) - def _end_of_century(self): + def _end_of_century(self) -> "DateTime": """ Reset the date to the last day of the century and the time to 23:59:59.999999. - - :rtype: DateTime """ year = self.year - 1 - (self.year - 1) % YEARS_PER_CENTURY + YEARS_PER_CENTURY return self.set(year, 12, 31, 23, 59, 59, 999999) - def _start_of_week(self): + def _start_of_week(self) -> "DateTime": """ Reset the date to the first day of the week and the time to 00:00:00. - - :rtype: DateTime """ dt = self @@ -1033,12 +805,10 @@ def _start_of_week(self): return dt.start_of("day") - def _end_of_week(self): + def _end_of_week(self) -> "DateTime": """ Reset the date to the last day of the week and the time to 23:59:59. - - :rtype: DateTime """ dt = self @@ -1047,20 +817,14 @@ def _end_of_week(self): return dt.end_of("day") - def next(self, day_of_week=None, keep_time=False): + def next( + self, day_of_week: Optional[int] = None, keep_time: bool = False + ) -> "DateTime": """ Modify to the next occurrence of a given day of the week. If no day_of_week is provided, modify to the next occurrence of the current day of the week. Use the supplied consts to indicate the desired day_of_week, ex. DateTime.MONDAY. - - :param day_of_week: The next day of week to reset to. - :type day_of_week: int or None - - :param keep_time: Whether to keep the time information or not. - :type keep_time: bool - - :rtype: DateTime """ if day_of_week is None: day_of_week = self.day_of_week @@ -1079,20 +843,14 @@ def next(self, day_of_week=None, keep_time=False): return dt - def previous(self, day_of_week=None, keep_time=False): + def previous( + self, day_of_week: Optional[int] = None, keep_time: bool = False + ) -> "DateTime": """ Modify to the previous occurrence of a given day of the week. If no day_of_week is provided, modify to the previous occurrence of the current day of the week. Use the supplied consts to indicate the desired day_of_week, ex. DateTime.MONDAY. - - :param day_of_week: The previous day of week to reset to. - :type day_of_week: int or None - - :param keep_time: Whether to keep the time information or not. - :type keep_time: bool - - :rtype: DateTime """ if day_of_week is None: day_of_week = self.day_of_week @@ -1111,7 +869,7 @@ def previous(self, day_of_week=None, keep_time=False): return dt - def first_of(self, unit, day_of_week=None): + def first_of(self, unit: str, day_of_week: Optional[int] = None) -> "DateTime": """ Returns an instance set to the first occurrence of a given day of the week in the current unit. @@ -1119,20 +877,13 @@ def first_of(self, unit, day_of_week=None): Use the supplied consts to indicate the desired day_of_week, ex. DateTime.MONDAY. Supported units are month, quarter and year. - - :param unit: The unit to use - :type unit: str - - :type day_of_week: int or None - - :rtype: DateTime """ if unit not in ["month", "quarter", "year"]: raise ValueError('Invalid unit "{}" for first_of()'.format(unit)) return getattr(self, "_first_of_{}".format(unit))(day_of_week) - def last_of(self, unit, day_of_week=None): + def last_of(self, unit: str, day_of_week: Optional[int] = None) -> "DateTime": """ Returns an instance set to the last occurrence of a given day of the week in the current unit. @@ -1140,20 +891,13 @@ def last_of(self, unit, day_of_week=None): Use the supplied consts to indicate the desired day_of_week, ex. DateTime.MONDAY. Supported units are month, quarter and year. - - :param unit: The unit to use - :type unit: str - - :type day_of_week: int or None - - :rtype: DateTime """ if unit not in ["month", "quarter", "year"]: raise ValueError('Invalid unit "{}" for first_of()'.format(unit)) return getattr(self, "_last_of_{}".format(unit))(day_of_week) - def nth_of(self, unit, nth, day_of_week): + def nth_of(self, unit: str, nth: int, day_of_week: int) -> "DateTime": """ Returns a new instance set to the given occurrence of a given day of the week in the current unit. @@ -1162,15 +906,6 @@ def nth_of(self, unit, nth, day_of_week): to indicate the desired day_of_week, ex. DateTime.MONDAY. Supported units are month, quarter and year. - - :param unit: The unit to use - :type unit: str - - :type nth: int - - :type day_of_week: int or None - - :rtype: DateTime """ if unit not in ["month", "quarter", "year"]: raise ValueError('Invalid unit "{}" for first_of()'.format(unit)) @@ -1185,16 +920,12 @@ def nth_of(self, unit, nth, day_of_week): return dt - def _first_of_month(self, day_of_week): + def _first_of_month(self, day_of_week: Optional[int] = None) -> "DateTime": """ Modify to the first occurrence of a given day of the week in the current month. If no day_of_week is provided, modify to the first day of the month. Use the supplied consts to indicate the desired day_of_week, ex. DateTime.MONDAY. - - :type day_of_week: int - - :rtype: DateTime """ dt = self.start_of("day") @@ -1212,16 +943,12 @@ def _first_of_month(self, day_of_week): return dt.set(day=day_of_month) - def _last_of_month(self, day_of_week=None): + def _last_of_month(self, day_of_week: Optional[int] = None) -> "DateTime": """ Modify to the last occurrence of a given day of the week in the current month. If no day_of_week is provided, modify to the last day of the month. Use the supplied consts to indicate the desired day_of_week, ex. DateTime.MONDAY. - - :type day_of_week: int or None - - :rtype: DateTime """ dt = self.start_of("day") @@ -1239,19 +966,13 @@ def _last_of_month(self, day_of_week=None): return dt.set(day=day_of_month) - def _nth_of_month(self, nth, day_of_week): + def _nth_of_month(self, nth: int, day_of_week: Optional[int] = None) -> "DateTime": """ Modify to the given occurrence of a given day of the week in the current month. If the calculated occurrence is outside, the scope of the current month, then return False and no modifications are made. Use the supplied consts to indicate the desired day_of_week, ex. DateTime.MONDAY. - - :type nth: int - - :type day_of_week: int or None - - :rtype: DateTime """ if nth == 1: return self.first_of("month", day_of_week) @@ -1266,35 +987,29 @@ def _nth_of_month(self, nth, day_of_week): return False - def _first_of_quarter(self, day_of_week=None): + def _first_of_quarter(self, day_of_week: Optional[int] = None) -> "DateTime": """ Modify to the first occurrence of a given day of the week in the current quarter. If no day_of_week is provided, modify to the first day of the quarter. Use the supplied consts to indicate the desired day_of_week, ex. DateTime.MONDAY. - - :type day_of_week: int or None - - :rtype: DateTime """ return self.on(self.year, self.quarter * 3 - 2, 1).first_of( "month", day_of_week ) - def _last_of_quarter(self, day_of_week=None): + def _last_of_quarter(self, day_of_week: Optional[int] = None) -> "DateTime": """ Modify to the last occurrence of a given day of the week in the current quarter. If no day_of_week is provided, modify to the last day of the quarter. Use the supplied consts to indicate the desired day_of_week, ex. DateTime.MONDAY. - - :type day_of_week: int or None - - :rtype: DateTime """ return self.on(self.year, self.quarter * 3, 1).last_of("month", day_of_week) - def _nth_of_quarter(self, nth, day_of_week): + def _nth_of_quarter( + self, nth: int, day_of_week: Optional[int] = None + ) -> "DateTime": """ Modify to the given occurrence of a given day of the week in the current quarter. If the calculated occurrence is outside, @@ -1323,45 +1038,31 @@ def _nth_of_quarter(self, nth, day_of_week): return self.on(self.year, dt.month, dt.day).start_of("day") - def _first_of_year(self, day_of_week=None): + def _first_of_year(self, day_of_week: Optional[int] = None) -> "DateTime": """ Modify to the first occurrence of a given day of the week in the current year. If no day_of_week is provided, modify to the first day of the year. Use the supplied consts to indicate the desired day_of_week, ex. DateTime.MONDAY. - - :type day_of_week: int or None - - :rtype: DateTime """ return self.set(month=1).first_of("month", day_of_week) - def _last_of_year(self, day_of_week=None): + def _last_of_year(self, day_of_week: Optional[int] = None) -> "DateTime": """ Modify to the last occurrence of a given day of the week in the current year. If no day_of_week is provided, modify to the last day of the year. Use the supplied consts to indicate the desired day_of_week, ex. DateTime.MONDAY. - - :type day_of_week: int or None - - :rtype: DateTime """ return self.set(month=MONTHS_PER_YEAR).last_of("month", day_of_week) - def _nth_of_year(self, nth, day_of_week): + def _nth_of_year(self, nth: int, day_of_week: Optional[int] = None) -> "DateTime": """ Modify to the given occurrence of a given day of the week in the current year. If the calculated occurrence is outside, the scope of the current year, then return False and no modifications are made. Use the supplied consts to indicate the desired day_of_week, ex. DateTime.MONDAY. - - :type nth: int - - :type day_of_week: int or None - - :rtype: DateTime """ if nth == 1: return self.first_of("year", day_of_week) @@ -1376,7 +1077,7 @@ def _nth_of_year(self, nth, day_of_week): return self.on(self.year, dt.month, dt.day).start_of("day") - def average(self, dt=None): + def average(self, dt: Optional[datetime.datetime] = None) -> "DateTime": """ Modify the current instance to the average of a given instance (default now) and the current instance. @@ -1393,7 +1094,9 @@ def average(self, dt=None): microseconds=(diff.in_seconds() * 1000000 + diff.microseconds) // 2 ) - def __sub__(self, other): + def __sub__( + self, other: Union[datetime.datetime, datetime.timedelta] + ) -> Union["DateTime", Period]: if isinstance(other, datetime.timedelta): return self._subtract_timedelta(other) @@ -1416,7 +1119,7 @@ def __sub__(self, other): return other.diff(self, False) - def __rsub__(self, other): + def __rsub__(self, other: datetime.datetime) -> Period: if not isinstance(other, datetime.datetime): return NotImplemented @@ -1436,47 +1139,49 @@ def __rsub__(self, other): return self.diff(other, False) - def __add__(self, other): + def __add__(self, other: datetime.timedelta) -> "DateTime": if not isinstance(other, datetime.timedelta): return NotImplemented return self._add_timedelta_(other) - def __radd__(self, other): + def __radd__(self, other: datetime.timedelta) -> "DateTime": return self.__add__(other) # Native methods override @classmethod - def fromtimestamp(cls, t, tz=None): + def fromtimestamp( + cls, t: float, tz: Optional[datetime.tzinfo] = None + ) -> "DateTime": return pendulum.instance(datetime.datetime.fromtimestamp(t, tz=tz), tz=tz) @classmethod - def utcfromtimestamp(cls, t): + def utcfromtimestamp(cls, t: float) -> "DateTime": return pendulum.instance(datetime.datetime.utcfromtimestamp(t), tz=None) @classmethod - def fromordinal(cls, n): + def fromordinal(cls, n) -> "DateTime": return pendulum.instance(datetime.datetime.fromordinal(n), tz=None) @classmethod - def combine(cls, date, time): + def combine(cls, date: datetime.date, time: datetime.time) -> "DateTime": return pendulum.instance(datetime.datetime.combine(date, time), tz=None) - def astimezone(self, tz=None): + def astimezone(self, tz: Optional[datetime.tzinfo] = None) -> "DateTime": return pendulum.instance(super(DateTime, self).astimezone(tz)) def replace( self, - year=None, - month=None, - day=None, - hour=None, - minute=None, - second=None, - microsecond=None, - tzinfo=True, - fold=None, + year: Optional[int] = None, + month: Optional[int] = None, + day: Optional[int] = None, + hour: Optional[int] = None, + minute: Optional[int] = None, + second: Optional[int] = None, + microsecond: Optional[int] = None, + tzinfo: Optional[Union[bool, datetime.tzinfo]] = True, + fold: Optional[int] = None, ): if year is None: year = self.year @@ -1515,10 +1220,10 @@ def replace( dst_rule=transition_rule, ) - def __getnewargs__(self): + def __getnewargs__(self) -> Tuple: return (self,) - def _getstate(self, protocol=3): + def _getstate(self, protocol: int = 3) -> Tuple: return ( self.year, self.month, @@ -1530,20 +1235,15 @@ def _getstate(self, protocol=3): self.tzinfo, ) - def __reduce__(self): + def __reduce__(self) -> Tuple: return self.__reduce_ex__(2) - def __reduce_ex__(self, protocol): + def __reduce_ex__(self, protocol: int) -> Tuple: return self.__class__, self._getstate(protocol) - def _cmp(self, other, **kwargs): + def _cmp(self, other: datetime.datetime, **kwargs) -> int: # Fix for pypy which compares using this method # which would lead to infinite recursion if we didn't override - kwargs = {"tzinfo": self.tz} - - if _HAS_FOLD: - kwargs["fold"] = self.fold - dt = datetime.datetime( self.year, self.month, @@ -1552,12 +1252,13 @@ def _cmp(self, other, **kwargs): self.minute, self.second, self.microsecond, - **kwargs + tzinfo=self.tz, + fold=self.fold, ) return 0 if dt == other else 1 if dt > other else -1 -DateTime.min = DateTime(1, 1, 1, 0, 0, tzinfo=UTC) -DateTime.max = DateTime(9999, 12, 31, 23, 59, 59, 999999, tzinfo=UTC) -DateTime.EPOCH = DateTime(1970, 1, 1) +DateTime.min: DateTime = DateTime(1, 1, 1, 0, 0, tzinfo=UTC) +DateTime.max: DateTime = DateTime(9999, 12, 31, 23, 59, 59, 999999, tzinfo=UTC) +DateTime.EPOCH: DateTime = DateTime(1970, 1, 1) diff --git a/pendulum/duration.py b/pendulum/duration.py index 45df13da..9bbd5080 100644 --- a/pendulum/duration.py +++ b/pendulum/duration.py @@ -6,7 +6,6 @@ import pendulum from pendulum.utils._compat import PYPY -from pendulum.utils._compat import decode from .constants import SECONDS_PER_DAY from .constants import SECONDS_PER_HOUR @@ -256,7 +255,7 @@ def in_words(self, locale=None, separator=" "): translation = locale.translation(unit) parts.append(translation.format(count)) - return decode(separator.join(parts)) + return separator.join(parts) def _sign(self, value): if value < 0: diff --git a/pendulum/formatting/difference_formatter.py b/pendulum/formatting/difference_formatter.py index 9be4b962..6455fe3d 100644 --- a/pendulum/formatting/difference_formatter.py +++ b/pendulum/formatting/difference_formatter.py @@ -2,8 +2,6 @@ import pendulum -from pendulum.utils._compat import decode - from ..locales.locale import Locale @@ -146,8 +144,8 @@ def format( else: key += ".before" - return locale.get(key).format(decode(time)) + return locale.get(key).format(time) key += ".{}".format(locale.plural(count)) - return decode(locale.get(key).format(count)) + return locale.get(key).format(count) diff --git a/pendulum/formatting/formatter.py b/pendulum/formatting/formatter.py index 9b64fcd6..b05b263b 100644 --- a/pendulum/formatting/formatter.py +++ b/pendulum/formatting/formatter.py @@ -8,7 +8,6 @@ import pendulum from pendulum.locales.locale import Locale -from pendulum.utils._compat import decode _MATCH_1 = r"\d" @@ -260,7 +259,7 @@ def format( fmt, ) - return decode(result) + return result def _format_token( self, dt, token, locale @@ -680,6 +679,6 @@ def _replace_tokens(self, token, locale): # type: (str, Locale) -> str if not isinstance(candidates, tuple): candidates = (candidates,) - pattern = "(?P<{}>{})".format(token, "|".join([decode(p) for p in candidates])) + pattern = "(?P<{}>{})".format(token, "|".join(candidates)) return pattern diff --git a/pendulum/locales/locale.py b/pendulum/locales/locale.py index de4cd82e..4e22290d 100644 --- a/pendulum/locales/locale.py +++ b/pendulum/locales/locale.py @@ -6,12 +6,10 @@ from importlib import import_module from typing import Any +from typing import Dict from typing import Optional from typing import Union -from pendulum.utils._compat import basestring -from pendulum.utils._compat import decode - class Locale: """ @@ -20,13 +18,13 @@ class Locale: _cache = {} - def __init__(self, locale, data): # type: (str, Any) -> None + def __init__(self, locale: str, data: Any) -> None: self._locale = locale self._data = data self._key_cache = {} @classmethod - def load(cls, locale): # type: (Union[str, Locale]) -> Locale + def load(cls, locale: Union[str, "Locale"]) -> "Locale": if isinstance(locale, Locale): return locale @@ -50,14 +48,14 @@ def load(cls, locale): # type: (Union[str, Locale]) -> Locale return cls._cache[locale] @classmethod - def normalize_locale(cls, locale): # type: (str) -> str + def normalize_locale(cls, locale: str) -> str: m = re.match("([a-z]{2})[-_]([a-z]{2})", locale, re.I) if m: return "{}_{}".format(m.group(1).lower(), m.group(2).lower()) else: return locale.lower() - def get(self, key, default=None): # type: (str, Optional[Any]) -> Any + def get(self, key: str, default: Optional[Any] = None) -> Any: if key in self._key_cache: return self._key_cache[key] @@ -69,36 +67,33 @@ def get(self, key, default=None): # type: (str, Optional[Any]) -> Any except KeyError: result = default - if isinstance(result, basestring): - result = decode(result) - self._key_cache[key] = result return self._key_cache[key] - def translation(self, key): # type: (str) -> Any + def translation(self, key: str) -> Any: return self.get("translations.{}".format(key)) - def plural(self, number): # type: (int) -> str - return decode(self._data["plural"](number)) + def plural(self, number: int) -> str: + return self._data["plural"](number) - def ordinal(self, number): # type: (int) -> str - return decode(self._data["ordinal"](number)) + def ordinal(self, number: int) -> str: + return self._data["ordinal"](number) - def ordinalize(self, number): # type: (int) -> str + def ordinalize(self, number: int) -> str: ordinal = self.get("custom.ordinal.{}".format(self.ordinal(number))) if not ordinal: - return decode("{}".format(number)) + return "{}".format(number) - return decode("{}{}".format(number, ordinal)) + return "{}{}".format(number, ordinal) - def match_translation(self, key, value): + def match_translation(self, key: str, value: Any) -> Optional[Dict]: translations = self.translation(key) if value not in translations.values(): return None return {v: k for k, v in translations.items()}[value] - def __repr__(self): + def __repr__(self) -> str: return "{}('{}')".format(self.__class__.__name__, self._locale) diff --git a/pendulum/period.py b/pendulum/period.py index c66c6b9d..de0bd39f 100644 --- a/pendulum/period.py +++ b/pendulum/period.py @@ -8,9 +8,6 @@ import pendulum -from pendulum.utils._compat import _HAS_FOLD -from pendulum.utils._compat import decode - from .constants import MONTHS_PER_YEAR from .duration import Duration from .helpers import precise_diff @@ -38,56 +35,32 @@ def __new__(cls, start, end, absolute=False): _start = start _end = end if isinstance(start, pendulum.DateTime): - if _HAS_FOLD: - _start = datetime( - start.year, - start.month, - start.day, - start.hour, - start.minute, - start.second, - start.microsecond, - tzinfo=start.tzinfo, - fold=start.fold, - ) - else: - _start = datetime( - start.year, - start.month, - start.day, - start.hour, - start.minute, - start.second, - start.microsecond, - tzinfo=start.tzinfo, - ) + _start = datetime( + start.year, + start.month, + start.day, + start.hour, + start.minute, + start.second, + start.microsecond, + tzinfo=start.tzinfo, + fold=start.fold, + ) elif isinstance(start, pendulum.Date): _start = date(start.year, start.month, start.day) if isinstance(end, pendulum.DateTime): - if _HAS_FOLD: - _end = datetime( - end.year, - end.month, - end.day, - end.hour, - end.minute, - end.second, - end.microsecond, - tzinfo=end.tzinfo, - fold=end.fold, - ) - else: - _end = datetime( - end.year, - end.month, - end.day, - end.hour, - end.minute, - end.second, - end.microsecond, - tzinfo=end.tzinfo, - ) + _end = datetime( + end.year, + end.month, + end.day, + end.hour, + end.minute, + end.second, + end.microsecond, + tzinfo=end.tzinfo, + fold=end.fold, + ) elif isinstance(end, pendulum.Date): _end = date(end.year, end.month, end.day) @@ -279,7 +252,7 @@ def in_words(self, locale=None, separator=" "): translation = locale.translation(unit) parts.append(translation.format(count)) - return decode(separator.join(parts)) + return separator.join(parts) def range(self, unit, amount=1): method = "add" diff --git a/pendulum/tz/timezone.py b/pendulum/tz/timezone.py index 62810130..04b46e59 100644 --- a/pendulum/tz/timezone.py +++ b/pendulum/tz/timezone.py @@ -5,11 +5,8 @@ from typing import TypeVar from typing import overload -import pendulum - from pendulum.helpers import local_time from pendulum.helpers import timestamp -from pendulum.utils._compat import _HAS_FOLD from .exceptions import AmbiguousTime from .exceptions import NonExistingTime @@ -79,14 +76,8 @@ def datetime( """ Return a normalized datetime for the current timezone. """ - if _HAS_FOLD: - return self.convert( - datetime(year, month, day, hour, minute, second, microsecond, fold=1) - ) - return self.convert( - datetime(year, month, day, hour, minute, second, microsecond), - dst_rule=POST_TRANSITION, + datetime(year, month, day, hour, minute, second, microsecond, fold=1) ) def _normalize(self, dt, dst_rule=None): # type: (_D, Optional[str]) -> _D @@ -94,9 +85,6 @@ def _normalize(self, dt, dst_rule=None): # type: (_D, Optional[str]) -> _D fold = 0 transition = self._lookup_transition(sec) - if not _HAS_FOLD and dst_rule is None: - dst_rule = POST_TRANSITION - if dst_rule is None: dst_rule = PRE_TRANSITION if dt.fold == 1: @@ -135,11 +123,7 @@ def _normalize(self, dt, dst_rule=None): # type: (_D, Optional[str]) -> _D else: sec -= transition.fix - kwargs = {"tzinfo": self} - if _HAS_FOLD or isinstance(dt, pendulum.DateTime): - kwargs["fold"] = fold - - return dt.__class__(*local_time(sec, 0, dt.microsecond), **kwargs) + return dt.__class__(*local_time(sec, 0, dt.microsecond), tzinfo=self, fold=fold) def _convert(self, dt): # type: (_D) -> _D if dt.tzinfo is self: @@ -175,12 +159,9 @@ def _convert(self, dt): # type: (_D) -> _D stamp += offset fold = int(not transition.ttype.is_dst()) - kwargs = {"tzinfo": self} - - if _HAS_FOLD or isinstance(dt, pendulum.DateTime): - kwargs["fold"] = fold - - return dt.__class__(*local_time(stamp, 0, dt.microsecond), **kwargs) + return dt.__class__( + *local_time(stamp, 0, dt.microsecond), tzinfo=self, fold=fold + ) def _lookup_transition( self, stamp, is_utc=False @@ -316,29 +297,17 @@ def offset(self): # type: () -> int return self._offset def _normalize(self, dt, dst_rule=None): # type: (_D, Optional[str]) -> _D - if _HAS_FOLD: - dt = dt.__class__( - dt.year, - dt.month, - dt.day, - dt.hour, - dt.minute, - dt.second, - dt.microsecond, - tzinfo=self, - fold=0, - ) - else: - dt = dt.__class__( - dt.year, - dt.month, - dt.day, - dt.hour, - dt.minute, - dt.second, - dt.microsecond, - tzinfo=self, - ) + dt = dt.__class__( + dt.year, + dt.month, + dt.day, + dt.hour, + dt.minute, + dt.second, + dt.microsecond, + tzinfo=self, + fold=0, + ) return dt diff --git a/pendulum/tz/zoneinfo/reader.py b/pendulum/tz/zoneinfo/reader.py index f4c1fa60..45afdbb3 100644 --- a/pendulum/tz/zoneinfo/reader.py +++ b/pendulum/tz/zoneinfo/reader.py @@ -13,8 +13,6 @@ from pytzdata.exceptions import TimezoneNotFound -from pendulum.utils._compat import PY2 - from .exceptions import InvalidTimezone from .exceptions import InvalidZoneinfoFile from .posix_timezone import PosixTimezone @@ -78,9 +76,6 @@ def _check_read(self, fd, nbytes): # type: (...) -> bytes "but got {}".format(nbytes, fd.name, len(result) if result else 0) ) - if PY2: - return bytearray(result) - return result def _parse(self, fd): # type: (...) -> Timezone diff --git a/pendulum/tz/zoneinfo/transition_type.py b/pendulum/tz/zoneinfo/transition_type.py index c2c33c67..a1e79dcc 100644 --- a/pendulum/tz/zoneinfo/transition_type.py +++ b/pendulum/tz/zoneinfo/transition_type.py @@ -1,8 +1,5 @@ from datetime import timedelta -from pendulum.utils._compat import PY2 -from pendulum.utils._compat import encode - class TransitionType: def __init__(self, offset, is_dst, abbr): @@ -18,9 +15,6 @@ def offset(self): # type: () -> int @property def abbreviation(self): # type: () -> str - if PY2: - return encode(self._abbr) - return self._abbr def is_dst(self): # type: () -> bool diff --git a/pendulum/utils/_compat.py b/pendulum/utils/_compat.py index 4893979b..11b36acf 100644 --- a/pendulum/utils/_compat.py +++ b/pendulum/utils/_compat.py @@ -1,54 +1,4 @@ import sys -PY2 = sys.version_info < (3, 0) -PY36 = sys.version_info >= (3, 6) PYPY = hasattr(sys, "pypy_version_info") - -_HAS_FOLD = PY36 - - -try: # Python 2 - long = long - unicode = unicode - basestring = basestring -except NameError: # Python 3 - long = int - unicode = str - basestring = str - - -def decode(string, encodings=None): - if not PY2 and not isinstance(string, bytes): - return string - - if PY2 and isinstance(string, unicode): - return string - - encodings = encodings or ["utf-8", "latin1", "ascii"] - - for encoding in encodings: - try: - return string.decode(encoding) - except (UnicodeEncodeError, UnicodeDecodeError): - pass - - return string.decode(encodings[0], errors="ignore") - - -def encode(string, encodings=None): - if not PY2 and isinstance(string, bytes): - return string - - if PY2 and isinstance(string, str): - return string - - encodings = encodings or ["utf-8", "latin1", "ascii"] - - for encoding in encodings: - try: - return string.encode(encoding) - except (UnicodeEncodeError, UnicodeDecodeError): - pass - - return string.encode(encodings[0], errors="ignore") diff --git a/poetry.lock b/poetry.lock index b9120f42..d04fcf70 100644 --- a/poetry.lock +++ b/poetry.lock @@ -50,18 +50,6 @@ version = "2.8.0" [package.dependencies] pytz = ">=2015.7" -[[package]] -category = "dev" -description = "Backport of functools.lru_cache" -name = "backports.functools-lru-cache" -optional = false -python-versions = ">=2.6" -version = "1.6.1" - -[package.extras] -docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] -testing = ["pytest (>=3.5,<3.7.3 || >3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-black-multipy", "pytest-cov"] - [[package]] category = "dev" description = "The uncompromising code formatter." @@ -87,11 +75,8 @@ category = "dev" description = "Validate configuration and produce human readable error messages." name = "cfgv" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "2.0.1" - -[package.dependencies] -six = "*" +python-versions = ">=3.6" +version = "3.0.0" [[package]] category = "dev" @@ -124,9 +109,6 @@ version = "0.6.2" pastel = ">=0.2.0,<0.3.0" pylev = ">=1.3,<2.0" crashtest = {version = ">=0.3.0,<0.4.0", markers = "python_version >= \"3.6\" and python_version < \"4.0\""} -enum34 = {version = ">=1.1,<2.0", markers = "python_version >= \"2.7\" and python_version < \"2.8\""} -typing = {version = ">=3.6,<4.0", markers = "python_version >= \"2.7\" and python_version < \"2.8\" or python_version >= \"3.4\" and python_version < \"3.5\""} -typing-extensions = {version = ">=3.6,<4.0", markers = "python_version >= \"3.5\" and python_full_version < \"3.5.4\""} [[package]] category = "dev" @@ -136,26 +118,6 @@ optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" version = "0.4.3" -[[package]] -category = "dev" -description = "Updated configparser from Python 3.7 for Python 2.6+." -name = "configparser" -optional = false -python-versions = ">=2.6" -version = "4.0.2" - -[package.extras] -docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] -testing = ["pytest (>=3.5,<3.7.3 || >3.7.3)", "pytest-checkdocs (>=1.2)", "pytest-flake8", "pytest-black-multipy"] - -[[package]] -category = "dev" -description = "Backports and enhancements for the contextlib module" -name = "contextlib2" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "0.6.0.post1" - [[package]] category = "dev" description = "Code coverage measurement for Python" @@ -183,14 +145,6 @@ optional = false python-versions = "*" version = "0.3.1" -[[package]] -category = "dev" -description = "Python 3.4 Enum backported to 3.3, 3.2, 3.1, 2.7, 2.6, 2.5, and 2.4" -name = "enum34" -optional = false -python-versions = "*" -version = "1.1.10" - [[package]] category = "dev" description = "A platform independent file lock." @@ -213,19 +167,11 @@ six = "*" [[package]] category = "dev" -description = "Python function signatures from PEP362 for Python 2.6, 2.7 and 3.2+" -name = "funcsigs" -optional = false -python-versions = "*" -version = "1.0.2" - -[[package]] -category = "dev" -description = "Backport of the concurrent.futures package from Python 3.2" -name = "futures" +description = "Clean single-source support for Python 3 and 2" +name = "future" optional = false -python-versions = "*" -version = "3.1.1" +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +version = "0.18.2" [[package]] category = "dev" @@ -248,9 +194,6 @@ version = "1.7.0" [package.dependencies] zipp = ">=0.5" -configparser = {version = ">=3.5", markers = "python_version < \"3\""} -contextlib2 = {version = "*", markers = "python_version < \"3\""} -pathlib2 = {version = "*", markers = "python_version < \"3\""} [package.extras] docs = ["sphinx", "rst.linker"] @@ -265,10 +208,6 @@ python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" version = "3.0.0" [package.dependencies] -contextlib2 = {version = "*", markers = "python_version < \"3\""} -pathlib2 = {version = "*", markers = "python_version < \"3\""} -singledispatch = {version = "*", markers = "python_version < \"3.4\""} -typing = {version = "*", markers = "python_version < \"3.5\""} zipp = {version = ">=0.4", markers = "python_version < \"3.8\""} [package.extras] @@ -302,6 +241,14 @@ MarkupSafe = ">=0.23" [package.extras] i18n = ["Babel (>=0.8)"] +[[package]] +category = "dev" +description = "Lightweight pipelining: using Python functions as pipeline jobs." +name = "joblib" +optional = false +python-versions = ">=3.6" +version = "0.16.0" + [[package]] category = "dev" description = "Python LiveReload is an awesome tool for web developers" @@ -314,13 +261,32 @@ version = "2.6.2" six = "*" tornado = {version = "*", markers = "python_version > \"2.7\""} +[[package]] +category = "dev" +description = "A Python implementation of Lunr.js" +name = "lunr" +optional = false +python-versions = "*" +version = "0.5.8" + +[package.dependencies] +future = ">=0.16.0" +six = ">=1.11.0" +nltk = {version = ">=3.2.5", optional = true, markers = "python_version > \"2.7\" and extra == \"languages\""} + +[package.extras] +languages = ["nltk (>=3.2.5,<3.5)", "nltk (>=3.2.5)"] + [[package]] category = "dev" description = "Python implementation of Markdown." name = "markdown" optional = false -python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*" -version = "3.1.1" +python-versions = ">=3.5" +version = "3.2.2" + +[package.dependencies] +importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} [package.extras] testing = ["coverage", "pyyaml"] @@ -349,27 +315,47 @@ category = "dev" description = "Project documentation with Markdown." name = "mkdocs" optional = false -python-versions = ">=2.7.9,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" -version = "1.0.4" +python-versions = ">=3.5" +version = "1.1.2" [package.dependencies] -Jinja2 = ">=2.7.1" -Markdown = ">=2.3.1" +Jinja2 = ">=2.10.1" +Markdown = ">=3.2.1" PyYAML = ">=3.10" click = ">=3.3" livereload = ">=2.5.1" tornado = ">=5.0" +lunr = {version = "0.5.8", extras = ["languages"]} [[package]] category = "dev" description = "More routines for operating on iterables, beyond itertools" name = "more-itertools" optional = false +python-versions = ">=3.5" +version = "8.4.0" + +[[package]] +category = "dev" +description = "Natural Language Toolkit" +name = "nltk" +optional = false python-versions = "*" -version = "5.0.0" +version = "3.5" [package.dependencies] -six = ">=1.0.0,<2.0.0" +click = "*" +joblib = "*" +regex = "*" +tqdm = "*" + +[package.extras] +all = ["requests", "numpy", "python-crfsuite", "scikit-learn", "twython", "pyparsing", "scipy", "matplotlib", "gensim"] +corenlp = ["requests"] +machine_learning = ["gensim", "numpy", "python-crfsuite", "scikit-learn", "scipy"] +plot = ["matplotlib"] +tgrep = ["pyparsing"] +twitter = ["twython"] [[package]] category = "dev" @@ -399,18 +385,6 @@ optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" version = "0.2.0" -[[package]] -category = "dev" -description = "Object-oriented filesystem paths" -name = "pathlib2" -optional = false -python-versions = "*" -version = "2.3.5" - -[package.dependencies] -six = "*" -scandir = {version = "*", markers = "python_version < \"3.5\""} - [[package]] category = "dev" description = "Utility library for gitignore style pattern matching of file paths." @@ -419,14 +393,6 @@ optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" version = "0.8.0" -[[package]] -category = "dev" -description = "Backport of PEP 562." -name = "pep562" -optional = false -python-versions = "*" -version = "1.0" - [[package]] category = "dev" description = "plugin and hook calling mechanisms for python" @@ -458,7 +424,6 @@ pyyaml = "*" six = "*" toml = "*" virtualenv = ">=15.2" -futures = {version = "*", markers = "python_version < \"3.2\""} importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} importlib-resources = {version = "*", markers = "python_version < \"3.7\""} @@ -475,8 +440,8 @@ category = "dev" description = "Pygments is a syntax highlighting package written in Python." name = "pygments" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "2.5.2" +python-versions = ">=3.5" +version = "2.6.1" [[package]] category = "dev" @@ -492,11 +457,10 @@ description = "Extension pack for Python Markdown." name = "pymdown-extensions" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*" -version = "6.2.1" +version = "6.3" [package.dependencies] -Markdown = ">=3.0.1" -pep562 = "*" +Markdown = ">=3.2" [[package]] category = "dev" @@ -523,17 +487,8 @@ py = ">=1.5.0" six = ">=1.10.0" wcwidth = "*" colorama = {version = "*", markers = "sys_platform == \"win32\" and python_version != \"3.4\""} -funcsigs = {version = ">=1.0", markers = "python_version < \"3.0\""} importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} -pathlib2 = {version = ">=2.2.0", markers = "python_version < \"3.6\""} - -[[package.dependencies.more-itertools]] -markers = "python_version <= \"2.7\"" -version = ">=4.0.0,<6.0.0" - -[[package.dependencies.more-itertools]] -markers = "python_version > \"2.7\"" -version = ">=4.0.0" +more-itertools = {version = ">=4.0.0", markers = "python_version > \"2.7\""} [package.extras] testing = ["argcomplete", "hypothesis (>=3.56)", "nose", "requests", "mock"] @@ -596,25 +551,6 @@ optional = false python-versions = "*" version = "2020.6.8" -[[package]] -category = "dev" -description = "scandir, a better directory iterator and faster os.walk()" -name = "scandir" -optional = false -python-versions = "*" -version = "1.10.0" - -[[package]] -category = "dev" -description = "This library brings functools.singledispatch from Python 3.4 to Python 2.6-3.3." -name = "singledispatch" -optional = false -python-versions = "*" -version = "3.4.0.3" - -[package.dependencies] -six = "*" - [[package]] category = "main" description = "Python 2 and 3 compatibility utilities" @@ -664,27 +600,22 @@ testing = ["freezegun (>=0.3.11)", "pathlib2 (>=2.3.3)", "pytest (>=4.0.0)", "py [[package]] category = "dev" -description = "a fork of Python 2 and 3 ast modules with type comment support" -name = "typed-ast" +description = "Fast, Extensible Progress Meter" +name = "tqdm" optional = false -python-versions = "*" -version = "1.4.1" +python-versions = ">=2.6, !=3.0.*, !=3.1.*" +version = "4.47.0" -[[package]] -category = "main" -description = "Type Hints for Python" -name = "typing" -optional = false -python-versions = "*" -version = "3.7.4.1" +[package.extras] +dev = ["py-make (>=0.1.0)", "twine", "argopt", "pydoc-markdown"] [[package]] category = "dev" -description = "Backported and Experimental Type Hints for Python 3.5+" -name = "typing-extensions" +description = "a fork of Python 2 and 3 ast modules with type comment support" +name = "typed-ast" optional = false python-versions = "*" -version = "3.7.4.2" +version = "1.4.1" [[package]] category = "dev" @@ -701,7 +632,6 @@ filelock = ">=3.0.0,<4" six = ">=1.9.0,<2" importlib-metadata = {version = ">=0.12,<2", markers = "python_version < \"3.8\""} importlib-resources = {version = ">=1.0", markers = "python_version < \"3.7\""} -pathlib2 = {version = ">=2.3.3,<3", markers = "python_version < \"3.4\" and sys_platform != \"win32\""} [package.extras] docs = ["sphinx (>=3)", "sphinx-argparse (>=0.2.5)", "sphinx-rtd-theme (>=0.4.3)", "towncrier (>=19.9.0rc1)", "proselint (>=0.10.2)"] @@ -715,27 +645,21 @@ optional = false python-versions = "*" version = "0.2.5" -[package.dependencies] -"backports.functools-lru-cache" = {version = ">=1.2.1", markers = "python_version < \"3.2\""} - [[package]] category = "dev" description = "Backport of pathlib-compatible object wrapper for zip files" name = "zipp" optional = false -python-versions = ">=2.7" -version = "1.2.0" - -[package.dependencies] -contextlib2 = {version = "*", markers = "python_version < \"3.4\""} +python-versions = ">=3.6" +version = "3.1.0" [package.extras] docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] -testing = ["pathlib2", "unittest2", "jaraco.itertools", "func-timeout"] +testing = ["jaraco.itertools", "func-timeout"] [metadata] -content-hash = "9a0542f32380e0fef3eb8d37b90903742a3fcad3b7e6b22f6ea8e4faac269e28" -python-versions = "~2.7 || ^3.5" +content-hash = "1803c93a845e7514237fbc9dc9afe73360a072dbd2c0df1107a5d41ae252baa4" +python-versions = "^3.6" [metadata.files] appdirs = [ @@ -758,17 +682,13 @@ babel = [ {file = "Babel-2.8.0-py2.py3-none-any.whl", hash = "sha256:d670ea0b10f8b723672d3a6abeb87b565b244da220d76b4dba1b66269ec152d4"}, {file = "Babel-2.8.0.tar.gz", hash = "sha256:1aac2ae2d0d8ea368fa90906567f5c08463d98ade155c0c4bfedd6a0f7160e38"}, ] -"backports.functools-lru-cache" = [ - {file = "backports.functools_lru_cache-1.6.1-py2.py3-none-any.whl", hash = "sha256:0bada4c2f8a43d533e4ecb7a12214d9420e66eb206d54bf2d682581ca4b80848"}, - {file = "backports.functools_lru_cache-1.6.1.tar.gz", hash = "sha256:8fde5f188da2d593bd5bc0be98d9abc46c95bb8a9dde93429570192ee6cc2d4a"}, -] black = [ {file = "black-19.10b0-py36-none-any.whl", hash = "sha256:1b30e59be925fafc1ee4565e5e08abef6b03fe455102883820fe5ee2e4734e0b"}, {file = "black-19.10b0.tar.gz", hash = "sha256:c2edb73a08e9e0e6f65a0e6af18b059b8b1cdd5bef997d7a0b181df93dc81539"}, ] cfgv = [ - {file = "cfgv-2.0.1-py2.py3-none-any.whl", hash = "sha256:fbd93c9ab0a523bf7daec408f3be2ed99a980e20b2d19b50fc184ca6b820d289"}, - {file = "cfgv-2.0.1.tar.gz", hash = "sha256:edb387943b665bf9c434f717bf630fa78aecd53d5900d2e05da6ad6048553144"}, + {file = "cfgv-3.0.0-py2.py3-none-any.whl", hash = "sha256:f22b426ed59cd2ab2b54ff96608d846c33dfb8766a67f0b4a6ce130ce244414f"}, + {file = "cfgv-3.0.0.tar.gz", hash = "sha256:04b093b14ddf9fd4d17c53ebfd55582d27b76ed30050193c14e560770c5360eb"}, ] cleo = [ {file = "cleo-0.8.1-py2.py3-none-any.whl", hash = "sha256:141cda6dc94a92343be626bb87a0b6c86ae291dfc732a57bf04310d4b4201753"}, @@ -786,14 +706,6 @@ colorama = [ {file = "colorama-0.4.3-py2.py3-none-any.whl", hash = "sha256:7d73d2a99753107a36ac6b455ee49046802e59d9d076ef8e47b61499fa29afff"}, {file = "colorama-0.4.3.tar.gz", hash = "sha256:e96da0d330793e2cb9485e9ddfd918d456036c7149416295932478192f4436a1"}, ] -configparser = [ - {file = "configparser-4.0.2-py2.py3-none-any.whl", hash = "sha256:254c1d9c79f60c45dfde850850883d5aaa7f19a23f13561243a050d5a7c3fe4c"}, - {file = "configparser-4.0.2.tar.gz", hash = "sha256:c7d282687a5308319bf3d2e7706e575c635b0a470342641c93bea0ea3b5331df"}, -] -contextlib2 = [ - {file = "contextlib2-0.6.0.post1-py2.py3-none-any.whl", hash = "sha256:3355078a159fbb44ee60ea80abd0d87b80b78c248643b49aa6d94673b413609b"}, - {file = "contextlib2-0.6.0.post1.tar.gz", hash = "sha256:01f490098c18b19d2bd5bb5dc445b2054d2fa97f09a4280ba2c5f3c394c8162e"}, -] coverage = [ {file = "coverage-5.2-cp27-cp27m-macosx_10_13_intel.whl", hash = "sha256:d9ad0a988ae20face62520785ec3595a5e64f35a21762a57d115dae0b8fb894a"}, {file = "coverage-5.2-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:4bb385a747e6ae8a65290b3df60d6c8a692a5599dc66c9fa3520e667886f2e10"}, @@ -838,11 +750,6 @@ distlib = [ {file = "distlib-0.3.1-py2.py3-none-any.whl", hash = "sha256:8c09de2c67b3e7deef7184574fc060ab8a793e7adbb183d942c389c8b13c52fb"}, {file = "distlib-0.3.1.zip", hash = "sha256:edf6116872c863e1aa9d5bb7cb5e05a022c519a4594dc703843343a9ddd9bff1"}, ] -enum34 = [ - {file = "enum34-1.1.10-py2-none-any.whl", hash = "sha256:a98a201d6de3f2ab3db284e70a33b0f896fbf35f8086594e8c9e74b909058d53"}, - {file = "enum34-1.1.10-py3-none-any.whl", hash = "sha256:c3858660960c984d6ab0ebad691265180da2b43f07e061c0f8dca9ef3cffd328"}, - {file = "enum34-1.1.10.tar.gz", hash = "sha256:cce6a7477ed816bd2542d03d53db9f0db935dd013b70f336a95c73979289f248"}, -] filelock = [ {file = "filelock-3.0.12-py3-none-any.whl", hash = "sha256:929b7d63ec5b7d6b71b0fa5ac14e030b3f70b75747cef1b10da9b879fef15836"}, {file = "filelock-3.0.12.tar.gz", hash = "sha256:18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59"}, @@ -851,14 +758,8 @@ freezegun = [ {file = "freezegun-0.3.15-py2.py3-none-any.whl", hash = "sha256:82c757a05b7c7ca3e176bfebd7d6779fd9139c7cb4ef969c38a28d74deef89b2"}, {file = "freezegun-0.3.15.tar.gz", hash = "sha256:e2062f2c7f95cc276a834c22f1a17179467176b624cc6f936e8bc3be5535ad1b"}, ] -funcsigs = [ - {file = "funcsigs-1.0.2-py2.py3-none-any.whl", hash = "sha256:330cc27ccbf7f1e992e69fef78261dc7c6569012cf397db8d3de0234e6c937ca"}, - {file = "funcsigs-1.0.2.tar.gz", hash = "sha256:a7bb0f2cf3a3fd1ab2732cb49eba4252c2af4240442415b4abce3b87022a8f50"}, -] -futures = [ - {file = "futures-3.1.1-py2-none-any.whl", hash = "sha256:c4884a65654a7c45435063e14ae85280eb1f111d94e542396717ba9828c4337f"}, - {file = "futures-3.1.1-py3-none-any.whl", hash = "sha256:3a44f286998ae64f0cc083682fcfec16c406134a81a589a5de445d7bb7c2751b"}, - {file = "futures-3.1.1.tar.gz", hash = "sha256:51ecb45f0add83c806c68e4b06106f90db260585b25ef2abfcda0bd95c0132fd"}, +future = [ + {file = "future-0.18.2.tar.gz", hash = "sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d"}, ] identify = [ {file = "identify-1.4.23-py2.py3-none-any.whl", hash = "sha256:882c4b08b4569517b5f2257ecca180e01f38400a17f429f5d0edff55530c41c7"}, @@ -880,12 +781,20 @@ jinja2 = [ {file = "Jinja2-2.11.2-py2.py3-none-any.whl", hash = "sha256:f0a4641d3cf955324a89c04f3d94663aa4d638abe8f733ecd3582848e1c37035"}, {file = "Jinja2-2.11.2.tar.gz", hash = "sha256:89aab215427ef59c34ad58735269eb58b1a5808103067f7bb9d5836c651b3bb0"}, ] +joblib = [ + {file = "joblib-0.16.0-py3-none-any.whl", hash = "sha256:d348c5d4ae31496b2aa060d6d9b787864dd204f9480baaa52d18850cb43e9f49"}, + {file = "joblib-0.16.0.tar.gz", hash = "sha256:8f52bf24c64b608bf0b2563e0e47d6fcf516abc8cfafe10cfd98ad66d94f92d6"}, +] livereload = [ {file = "livereload-2.6.2.tar.gz", hash = "sha256:d1eddcb5c5eb8d2ca1fa1f750e580da624c0f7fcb734aa5780dc81b7dcbd89be"}, ] +lunr = [ + {file = "lunr-0.5.8-py2.py3-none-any.whl", hash = "sha256:aab3f489c4d4fab4c1294a257a30fec397db56f0a50273218ccc3efdbf01d6ca"}, + {file = "lunr-0.5.8.tar.gz", hash = "sha256:c4fb063b98eff775dd638b3df380008ae85e6cb1d1a24d1cd81a10ef6391c26e"}, +] markdown = [ - {file = "Markdown-3.1.1-py2.py3-none-any.whl", hash = "sha256:56a46ac655704b91e5b7e6326ce43d5ef72411376588afa1dd90e881b83c7e8c"}, - {file = "Markdown-3.1.1.tar.gz", hash = "sha256:2e50876bcdd74517e7b71f3e7a76102050edec255b3983403f1a63e7c8a41e7a"}, + {file = "Markdown-3.2.2-py3-none-any.whl", hash = "sha256:c467cd6233885534bf0fe96e62e3cf46cfc1605112356c4f9981512b8174de59"}, + {file = "Markdown-3.2.2.tar.gz", hash = "sha256:1fafe3f1ecabfb514a5285fca634a53c1b32a81cb0feb154264d55bf2ff22c17"}, ] markdown-include = [ {file = "markdown-include-0.5.1.tar.gz", hash = "sha256:72a45461b589489a088753893bc95c5fa5909936186485f4ed55caa57d10250f"}, @@ -926,13 +835,15 @@ markupsafe = [ {file = "MarkupSafe-1.1.1.tar.gz", hash = "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b"}, ] mkdocs = [ - {file = "mkdocs-1.0.4-py2.py3-none-any.whl", hash = "sha256:8cc8b38325456b9e942c981a209eaeb1e9f3f77b493ad755bfef889b9c8d356a"}, - {file = "mkdocs-1.0.4.tar.gz", hash = "sha256:17d34329aad75d5de604b9ed4e31df3a4d235afefdc46ce7b1964fddb2e1e939"}, + {file = "mkdocs-1.1.2-py3-none-any.whl", hash = "sha256:096f52ff52c02c7e90332d2e53da862fde5c062086e1b5356a6e392d5d60f5e9"}, + {file = "mkdocs-1.1.2.tar.gz", hash = "sha256:f0b61e5402b99d7789efa032c7a74c90a20220a9c81749da06dbfbcbd52ffb39"}, ] more-itertools = [ - {file = "more-itertools-5.0.0.tar.gz", hash = "sha256:38a936c0a6d98a38bcc2d03fdaaedaba9f412879461dd2ceff8d37564d6522e4"}, - {file = "more_itertools-5.0.0-py2-none-any.whl", hash = "sha256:c0a5785b1109a6bd7fac76d6837fd1feca158e54e521ccd2ae8bfe393cc9d4fc"}, - {file = "more_itertools-5.0.0-py3-none-any.whl", hash = "sha256:fe7a7cae1ccb57d33952113ff4fa1bc5f879963600ed74918f1236e212ee50b9"}, + {file = "more-itertools-8.4.0.tar.gz", hash = "sha256:68c70cc7167bdf5c7c9d8f6954a7837089c6a36bf565383919bb595efb8a17e5"}, + {file = "more_itertools-8.4.0-py3-none-any.whl", hash = "sha256:b78134b2063dd214000685165d81c154522c3ee0a1c0d4d113c80361c234c5a2"}, +] +nltk = [ + {file = "nltk-3.5.zip", hash = "sha256:845365449cd8c5f9731f7cb9f8bd6fd0767553b9d53af9eb1b3abf7700936b35"}, ] nodeenv = [ {file = "nodeenv-1.4.0-py2.py3-none-any.whl", hash = "sha256:4b0b77afa3ba9b54f4b6396e60b0c83f59eaeb2d63dc3cc7a70f7f4af96c82bc"}, @@ -945,18 +856,10 @@ pastel = [ {file = "pastel-0.2.0-py2.py3-none-any.whl", hash = "sha256:18b559dc3ad4ba9b8bd5baebe6503f25f36d21460f021cf27a8d889cb5d17840"}, {file = "pastel-0.2.0.tar.gz", hash = "sha256:46155fc523bdd4efcd450bbcb3f2b94a6e3b25edc0eb493e081104ad09e1ca36"}, ] -pathlib2 = [ - {file = "pathlib2-2.3.5-py2.py3-none-any.whl", hash = "sha256:0ec8205a157c80d7acc301c0b18fbd5d44fe655968f5d947b6ecef5290fc35db"}, - {file = "pathlib2-2.3.5.tar.gz", hash = "sha256:6cd9a47b597b37cc57de1c05e56fb1a1c9cc9fab04fe78c29acd090418529868"}, -] pathspec = [ {file = "pathspec-0.8.0-py2.py3-none-any.whl", hash = "sha256:7d91249d21749788d07a2d0f94147accd8f845507400749ea19c1ec9054a12b0"}, {file = "pathspec-0.8.0.tar.gz", hash = "sha256:da45173eb3a6f2a5a487efba21f050af2b41948be6ab52b6a1e3ff22bb8b7061"}, ] -pep562 = [ - {file = "pep562-1.0-py2.py3-none-any.whl", hash = "sha256:d2a48b178ebf5f8dd31709cc26a19808ef794561fa2fe50ea01ea2bad4d667ef"}, - {file = "pep562-1.0.tar.gz", hash = "sha256:58cb1cc9ee63d93e62b4905a50357618d526d289919814bea1f0da8f53b79395"}, -] pluggy = [ {file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"}, {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"}, @@ -970,16 +873,16 @@ py = [ {file = "py-1.9.0.tar.gz", hash = "sha256:9ca6883ce56b4e8da7e79ac18787889fa5206c79dcc67fb065376cd2fe03f342"}, ] pygments = [ - {file = "Pygments-2.5.2-py2.py3-none-any.whl", hash = "sha256:2a3fe295e54a20164a9df49c75fa58526d3be48e14aceba6d6b1e8ac0bfd6f1b"}, - {file = "Pygments-2.5.2.tar.gz", hash = "sha256:98c8aa5a9f778fcd1026a17361ddaf7330d1b7c62ae97c3bb0ae73e0b9b6b0fe"}, + {file = "Pygments-2.6.1-py3-none-any.whl", hash = "sha256:ff7a40b4860b727ab48fad6360eb351cc1b33cbf9b15a0f689ca5353e9463324"}, + {file = "Pygments-2.6.1.tar.gz", hash = "sha256:647344a061c249a3b74e230c739f434d7ea4d8b1d5f3721bc0f3558049b38f44"}, ] pylev = [ {file = "pylev-1.3.0-py2.py3-none-any.whl", hash = "sha256:1d29a87beb45ebe1e821e7a3b10da2b6b2f4c79b43f482c2df1a1f748a6e114e"}, {file = "pylev-1.3.0.tar.gz", hash = "sha256:063910098161199b81e453025653ec53556c1be7165a9b7c50be2f4d57eae1c3"}, ] pymdown-extensions = [ - {file = "pymdown-extensions-6.2.1.tar.gz", hash = "sha256:3bbe6048275f8a0d13a0fe44e0ea201e67268aa7bb40c2544eef16abbf168f7b"}, - {file = "pymdown_extensions-6.2.1-py2.py3-none-any.whl", hash = "sha256:dce5e17b93be0572322b7d06c9a13c13a9d98694d6468277911d50ca87d26f29"}, + {file = "pymdown-extensions-6.3.tar.gz", hash = "sha256:cb879686a586b22292899771f5e5bc3382808e92aa938f71b550ecdea709419f"}, + {file = "pymdown_extensions-6.3-py2.py3-none-any.whl", hash = "sha256:66fae2683c7a1dac53184f7de57f51f8dad73f9ead2f453e94e85096cb811335"}, ] pyparsing = [ {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"}, @@ -1041,23 +944,6 @@ regex = [ {file = "regex-2020.6.8-cp38-cp38-win_amd64.whl", hash = "sha256:6ad8663c17db4c5ef438141f99e291c4d4edfeaacc0ce28b5bba2b0bf273d9b5"}, {file = "regex-2020.6.8.tar.gz", hash = "sha256:e9b64e609d37438f7d6e68c2546d2cb8062f3adb27e6336bc129b51be20773ac"}, ] -scandir = [ - {file = "scandir-1.10.0-cp27-cp27m-win32.whl", hash = "sha256:92c85ac42f41ffdc35b6da57ed991575bdbe69db895507af88b9f499b701c188"}, - {file = "scandir-1.10.0-cp27-cp27m-win_amd64.whl", hash = "sha256:cb925555f43060a1745d0a321cca94bcea927c50114b623d73179189a4e100ac"}, - {file = "scandir-1.10.0-cp34-cp34m-win32.whl", hash = "sha256:2c712840c2e2ee8dfaf36034080108d30060d759c7b73a01a52251cc8989f11f"}, - {file = "scandir-1.10.0-cp34-cp34m-win_amd64.whl", hash = "sha256:2586c94e907d99617887daed6c1d102b5ca28f1085f90446554abf1faf73123e"}, - {file = "scandir-1.10.0-cp35-cp35m-win32.whl", hash = "sha256:2b8e3888b11abb2217a32af0766bc06b65cc4a928d8727828ee68af5a967fa6f"}, - {file = "scandir-1.10.0-cp35-cp35m-win_amd64.whl", hash = "sha256:8c5922863e44ffc00c5c693190648daa6d15e7c1207ed02d6f46a8dcc2869d32"}, - {file = "scandir-1.10.0-cp36-cp36m-win32.whl", hash = "sha256:2ae41f43797ca0c11591c0c35f2f5875fa99f8797cb1a1fd440497ec0ae4b022"}, - {file = "scandir-1.10.0-cp36-cp36m-win_amd64.whl", hash = "sha256:7d2d7a06a252764061a020407b997dd036f7bd6a175a5ba2b345f0a357f0b3f4"}, - {file = "scandir-1.10.0-cp37-cp37m-win32.whl", hash = "sha256:67f15b6f83e6507fdc6fca22fedf6ef8b334b399ca27c6b568cbfaa82a364173"}, - {file = "scandir-1.10.0-cp37-cp37m-win_amd64.whl", hash = "sha256:b24086f2375c4a094a6b51e78b4cf7ca16c721dcee2eddd7aa6494b42d6d519d"}, - {file = "scandir-1.10.0.tar.gz", hash = "sha256:4d4631f6062e658e9007ab3149a9b914f3548cb38bfb021c64f39a025ce578ae"}, -] -singledispatch = [ - {file = "singledispatch-3.4.0.3-py2.py3-none-any.whl", hash = "sha256:833b46966687b3de7f438c761ac475213e53b306740f1abfaa86e1d1aae56aa8"}, - {file = "singledispatch-3.4.0.3.tar.gz", hash = "sha256:5b06af87df13818d14f08a028e42f566640aef80805c3b50c5056b086e3c2b9c"}, -] six = [ {file = "six-1.15.0-py2.py3-none-any.whl", hash = "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"}, {file = "six-1.15.0.tar.gz", hash = "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259"}, @@ -1081,6 +967,10 @@ tox = [ {file = "tox-3.16.1-py2.py3-none-any.whl", hash = "sha256:60c3793f8ab194097ec75b5a9866138444f63742b0f664ec80be1222a40687c5"}, {file = "tox-3.16.1.tar.gz", hash = "sha256:9a746cda9cadb9e1e05c7ab99f98cfcea355140d2ecac5f97520be94657c3bc7"}, ] +tqdm = [ + {file = "tqdm-4.47.0-py2.py3-none-any.whl", hash = "sha256:7810e627bcf9d983a99d9ff8a0c09674400fd2927eddabeadf153c14a2ec8656"}, + {file = "tqdm-4.47.0.tar.gz", hash = "sha256:63ef7a6d3eb39f80d6b36e4867566b3d8e5f1fe3d6cb50c5e9ede2b3198ba7b7"}, +] typed-ast = [ {file = "typed_ast-1.4.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:73d785a950fc82dd2a25897d525d003f6378d1cb23ab305578394694202a58c3"}, {file = "typed_ast-1.4.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:aaee9905aee35ba5905cfb3c62f3e83b3bec7b39413f0a7f19be4e547ea01ebb"}, @@ -1104,16 +994,6 @@ typed-ast = [ {file = "typed_ast-1.4.1-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:d43943ef777f9a1c42bf4e552ba23ac77a6351de620aa9acf64ad54933ad4d34"}, {file = "typed_ast-1.4.1.tar.gz", hash = "sha256:8c8aaad94455178e3187ab22c8b01a3837f8ee50e09cf31f1ba129eb293ec30b"}, ] -typing = [ - {file = "typing-3.7.4.1-py2-none-any.whl", hash = "sha256:c8cabb5ab8945cd2f54917be357d134db9cc1eb039e59d1606dc1e60cb1d9d36"}, - {file = "typing-3.7.4.1-py3-none-any.whl", hash = "sha256:f38d83c5a7a7086543a0f649564d661859c5146a85775ab90c0d2f93ffaa9714"}, - {file = "typing-3.7.4.1.tar.gz", hash = "sha256:91dfe6f3f706ee8cc32d38edbbf304e9b7583fb37108fef38229617f8b3eba23"}, -] -typing-extensions = [ - {file = "typing_extensions-3.7.4.2-py2-none-any.whl", hash = "sha256:f8d2bd89d25bc39dabe7d23df520442fa1d8969b82544370e03d88b5a591c392"}, - {file = "typing_extensions-3.7.4.2-py3-none-any.whl", hash = "sha256:6e95524d8a547a91e08f404ae485bbb71962de46967e1b71a0cb89af24e761c5"}, - {file = "typing_extensions-3.7.4.2.tar.gz", hash = "sha256:79ee589a3caca649a9bfd2a8de4709837400dfa00b6cc81962a1e6a1815969ae"}, -] virtualenv = [ {file = "virtualenv-20.0.26-py2.py3-none-any.whl", hash = "sha256:c11a475400e98450403c0364eb3a2d25d42f71cf1493da64390487b666de4324"}, {file = "virtualenv-20.0.26.tar.gz", hash = "sha256:e10cc66f40cbda459720dfe1d334c4dc15add0d80f09108224f171006a97a172"}, @@ -1123,6 +1003,6 @@ wcwidth = [ {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"}, ] zipp = [ - {file = "zipp-1.2.0-py2.py3-none-any.whl", hash = "sha256:e0d9e63797e483a30d27e09fffd308c59a700d365ec34e93cc100844168bf921"}, - {file = "zipp-1.2.0.tar.gz", hash = "sha256:c70410551488251b0fee67b460fb9a536af8d6f9f008ad10ac51f615b6a521b1"}, + {file = "zipp-3.1.0-py3-none-any.whl", hash = "sha256:aa36550ff0c0b7ef7fa639055d797116ee891440eac1a56f378e2d3179e0320b"}, + {file = "zipp-3.1.0.tar.gz", hash = "sha256:c599e4d75c98f6798c509911d08a22e6c021d074469042177c8c86fb92eefd96"}, ] diff --git a/pyproject.toml b/pyproject.toml index e7e461fe..b251d385 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "pendulum" -version = "2.1.1" +version = "3.0.0a0" description = "Python datetimes made easy" authors = ["Sébastien Eustace "] license = "MIT" @@ -25,13 +25,10 @@ include = [ [tool.poetry.dependencies] -python = "~2.7 || ^3.5" +python = "^3.6" python-dateutil = "^2.6" pytzdata = ">=2020.1" -# typing is needed for Python < 3.5 -typing = { version = "^3.6", python = "<3.5" } - [tool.poetry.dev-dependencies] pytest = "^4.6" pytest-cov = "^2.5" @@ -39,8 +36,8 @@ pytz = ">=2018.3" babel = "^2.5" cleo = "^0.8.1" tox = "^3.0" -black = { version = "^19.3b0", markers = "python_version >= '3.6' and python_version < '4.0' and implementation_name != 'pypy'" } -isort = { version = "^4.3.21", markers = "python_version >= '3.6' and python_version < '4.0'" } +black = { version = "^19.3b0", markers = "implementation_name != 'pypy'" } +isort = "^4.3.21" pre-commit = "^1.10" mkdocs = { version = "^1.0", python = "^3.5" } pymdown-extensions = "^6.0" diff --git a/tests/datetime/test_construct.py b/tests/datetime/test_construct.py index 2e45ead9..0bb8f0e5 100644 --- a/tests/datetime/test_construct.py +++ b/tests/datetime/test_construct.py @@ -11,7 +11,6 @@ from pendulum import DateTime from pendulum.tz import timezone -from pendulum.utils._compat import PY36 from ..conftest import assert_datetime @@ -104,7 +103,6 @@ def test_now(): assert now.hour != in_paris.hour -@pytest.mark.skipif(not PY36, reason="fold attribute only present in Python 3.6+") @freeze_time("2016-03-27 00:30:00") def test_now_dst_off(): utc = pendulum.now("UTC") @@ -115,7 +113,6 @@ def test_now_dst_off(): assert in_paris.isoformat() == in_paris_from_utc.isoformat() -@pytest.mark.skipif(not PY36, reason="fold attribute only present in Python 3.6+") @freeze_time("2016-03-27 01:30:00") def test_now_dst_transitioning_on(): utc = pendulum.now("UTC") @@ -126,7 +123,6 @@ def test_now_dst_transitioning_on(): assert in_paris.isoformat() == in_paris_from_utc.isoformat() -@pytest.mark.skipif(not PY36, reason="fold attribute only present in Python 3.6+") @freeze_time("2016-10-30 00:30:00") def test_now_dst_on(): utc = pendulum.now("UTC") @@ -137,7 +133,6 @@ def test_now_dst_on(): assert in_paris.isoformat() == in_paris_from_utc.isoformat() -@pytest.mark.skipif(not PY36, reason="fold attribute only present in Python 3.6+") @freeze_time("2016-10-30 01:30:00") def test_now_dst_transitioning_off(): utc = pendulum.now("UTC") diff --git a/tests/datetime/test_from_format.py b/tests/datetime/test_from_format.py index 398b68da..c079c120 100644 --- a/tests/datetime/test_from_format.py +++ b/tests/datetime/test_from_format.py @@ -1,8 +1,6 @@ import pendulum import pytest -from pendulum.utils._compat import PY2 - from ..conftest import assert_datetime @@ -154,11 +152,6 @@ def test_from_format(text, fmt, expected, now): else: now = pendulum.parse(now) - # Python 2.7 loses precision for x timestamps - # so we don't test - if fmt == "x" and PY2: - return - with pendulum.test(now): assert pendulum.from_format(text, fmt).isoformat() == expected diff --git a/tests/datetime/test_getters.py b/tests/datetime/test_getters.py index 264f4da5..70488d23 100644 --- a/tests/datetime/test_getters.py +++ b/tests/datetime/test_getters.py @@ -5,7 +5,6 @@ from pendulum import DateTime from pendulum.tz import timezone -from pendulum.utils._compat import _HAS_FOLD from ..conftest import assert_date from ..conftest import assert_time @@ -103,19 +102,10 @@ def test_timestamp_with_transition(): 2012, 10, 28, 2, 0, tz="Europe/Warsaw", dst_rule=pendulum.POST_TRANSITION ) - if _HAS_FOLD: - # the difference between the timestamps before and after is equal to one hour - assert d_post.timestamp() - d_pre.timestamp() == pendulum.SECONDS_PER_HOUR - assert d_post.float_timestamp - d_pre.float_timestamp == ( - pendulum.SECONDS_PER_HOUR - ) - assert d_post.int_timestamp - d_pre.int_timestamp == pendulum.SECONDS_PER_HOUR - else: - # when the transition is not recognizable - # then the difference should be equal to zero hours - assert d_post.timestamp() - d_pre.timestamp() == 0 - assert d_post.float_timestamp - d_pre.float_timestamp == 0 - assert d_post.int_timestamp - d_pre.int_timestamp == 0 + # the difference between the timestamps before and after is equal to one hour + assert d_post.timestamp() - d_pre.timestamp() == pendulum.SECONDS_PER_HOUR + assert d_post.float_timestamp - d_pre.float_timestamp == (pendulum.SECONDS_PER_HOUR) + assert d_post.int_timestamp - d_pre.int_timestamp == pendulum.SECONDS_PER_HOUR def test_age(): diff --git a/tests/tz/test_timezone.py b/tests/tz/test_timezone.py index 303a5fc9..8efae423 100644 --- a/tests/tz/test_timezone.py +++ b/tests/tz/test_timezone.py @@ -8,7 +8,6 @@ from pendulum.tz import fixed_timezone from pendulum.tz.exceptions import AmbiguousTime from pendulum.tz.exceptions import NonExistingTime -from pendulum.utils._compat import PY36 from ..conftest import assert_datetime @@ -22,7 +21,6 @@ def setup(): pendulum.tz._tz_cache = {} -@pytest.mark.skipif(not PY36, reason="fold attribute only present in Python 3.6+") def test_basic_convert(): dt = datetime(2016, 6, 1, 12, 34, 56, 123456, fold=1) tz = timezone("Europe/Paris") @@ -40,7 +38,6 @@ def test_basic_convert(): assert dt.tzinfo.dst(dt) == timedelta(seconds=3600) -@pytest.mark.skipif(not PY36, reason="fold attribute only present in Python 3.6+") def test_skipped_time_with_pre_rule(): dt = datetime(2013, 3, 31, 2, 30, 45, 123456, fold=0) tz = timezone("Europe/Paris") @@ -58,7 +55,6 @@ def test_skipped_time_with_pre_rule(): assert dt.tzinfo.dst(dt) == timedelta() -@pytest.mark.skipif(not PY36, reason="fold attribute only present in Python 3.6+") def test_skipped_time_with_post_rule(): dt = datetime(2013, 3, 31, 2, 30, 45, 123456, fold=1) tz = timezone("Europe/Paris") @@ -117,7 +113,6 @@ def test_skipped_time_with_error(): tz.convert(dt, dst_rule=pendulum.TRANSITION_ERROR) -@pytest.mark.skipif(not PY36, reason="fold attribute only present in Python 3.6+") def test_repeated_time(): dt = datetime(2013, 10, 27, 2, 30, 45, 123456, fold=1) tz = timezone("Europe/Paris") @@ -135,7 +130,6 @@ def test_repeated_time(): assert dt.tzinfo.dst(dt) == timedelta() -@pytest.mark.skipif(not PY36, reason="fold attribute only present in Python 3.6+") def test_repeated_time_explicit_post_rule(): dt = datetime(2013, 10, 27, 2, 30, 45, 123456) tz = timezone("Europe/Paris") @@ -153,7 +147,6 @@ def test_repeated_time_explicit_post_rule(): assert dt.tzinfo.dst(dt) == timedelta() -@pytest.mark.skipif(not PY36, reason="fold attribute only present in Python 3.6+") def test_repeated_time_pre_rule(): dt = datetime(2013, 10, 27, 2, 30, 45, 123456, fold=0) tz = timezone("Europe/Paris") @@ -171,7 +164,6 @@ def test_repeated_time_pre_rule(): assert dt.tzinfo.dst(dt) == timedelta(seconds=3600) -@pytest.mark.skipif(not PY36, reason="Disambiguation is not available in Python 2.7") def test_repeated_time_explicit_pre_rule(): dt = datetime(2013, 10, 27, 2, 30, 45, 123456) tz = timezone("Europe/Paris") @@ -389,7 +381,6 @@ def test_on_last_transition(): assert dt.utcoffset().total_seconds() == 7200 -@pytest.mark.skipif(not PY36, reason="fold attribute only present in Python 3.6+") def test_convert_fold_attribute_is_honored(): tz = pendulum.timezone("US/Eastern") dt = datetime(2014, 11, 2, 1, 30) @@ -401,7 +392,6 @@ def test_convert_fold_attribute_is_honored(): assert new.strftime("%z") == "-0500" -@pytest.mark.skipif(not PY36, reason="fold attribute only present in Python 3.6+") def test_utcoffset_fold_attribute_is_honored(): tz = pendulum.timezone("US/Eastern") dt = datetime(2014, 11, 2, 1, 30) @@ -415,7 +405,6 @@ def test_utcoffset_fold_attribute_is_honored(): assert offset.total_seconds() == -5 * 3600 -@pytest.mark.skipif(not PY36, reason="fold attribute only present in Python 3.6+") def test_dst_fold_attribute_is_honored(): tz = pendulum.timezone("US/Eastern") dt = datetime(2014, 11, 2, 1, 30) @@ -429,7 +418,6 @@ def test_dst_fold_attribute_is_honored(): assert offset.total_seconds() == 0 -@pytest.mark.skipif(not PY36, reason="fold attribute only present in Python 3.6+") def test_tzname_fold_attribute_is_honored(): tz = pendulum.timezone("US/Eastern") dt = datetime(2014, 11, 2, 1, 30) @@ -443,7 +431,6 @@ def test_tzname_fold_attribute_is_honored(): assert name == "EST" -@pytest.mark.skipif(not PY36, reason="fold attribute only present in Python 3.6+") def test_constructor_fold_attribute_is_honored(): tz = pendulum.timezone("US/Eastern") dt = datetime(2014, 11, 2, 1, 30, tzinfo=tz) @@ -455,7 +442,6 @@ def test_constructor_fold_attribute_is_honored(): assert dt.strftime("%z") == "-0500" -@pytest.mark.skipif(not PY36, reason="fold attribute only present in Python 3.6+") def test_convert_sets_fold_attribute_properly(): tz = pendulum.timezone("US/Eastern") diff --git a/tox.ini b/tox.ini index 12d63c9b..38c59819 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,6 @@ [tox] isolated_build = true -envlist = py27, py35, py36, py37, py38, pypy, pypy3 +envlist = py36, py37, py38, pypy3 [testenv] whitelist_externals = poetry