From aa121e18c546ef01eecedca9ef721a954fb76a43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Eustace?= Date: Fri, 17 Aug 2018 15:22:49 +0200 Subject: [PATCH 001/256] Fix from_format() with escaped elements --- CHANGELOG.md | 7 +++++++ pendulum/formatting/formatter.py | 3 +++ tests/datetime/test_from_format.py | 6 ++++++ 3 files changed, 16 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c4bccb2d..e5b5fc8c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Change Log +## [Unreleased] + +### Fixed + +- Fixed `from_format()` not recognizing input strings when the specified pattern had escaped elements. + + ## [2.0.3] - 2018-07-30 ### Fixed diff --git a/pendulum/formatting/formatter.py b/pendulum/formatting/formatter.py index 504cca77..924c802e 100644 --- a/pendulum/formatting/formatter.py +++ b/pendulum/formatting/formatter.py @@ -626,6 +626,9 @@ def _replace_tokens(self, token, locale): # type: (str, Locale) -> str if token.startswith("[") and token.endswith("]"): return token[1:-1] elif token.startswith("\\"): + if len(token) == 2 and token[1] in {"[", "]"}: + return "" + return token elif token not in self._REGEX_TOKENS and token not in self._LOCALIZABLE_TOKENS: raise ValueError("Unsupported token: {}".format(token)) diff --git a/tests/datetime/test_from_format.py b/tests/datetime/test_from_format.py index da1a38f8..480e2cf6 100644 --- a/tests/datetime/test_from_format.py +++ b/tests/datetime/test_from_format.py @@ -31,6 +31,12 @@ def test_from_format_with_timezone(): assert "Europe/London" == d.timezone_name +def test_from_format_with_escaped_elements(): + d = pendulum.from_format("1975-05-21T22:32:11+00:00", "YYYY-MM-DD[T]HH:mm:ssZ") + assert_datetime(d, 1975, 5, 21, 22, 32, 11) + assert "+00:00" == d.timezone_name + + def test_from_format_with_millis(): d = pendulum.from_format("1975-05-21 22:32:11.123456", "YYYY-MM-DD HH:mm:ss.SSSSSS") assert_datetime(d, 1975, 5, 21, 22, 32, 11, 123456) From 7de6f28de967ea51d1f283125ecf0f578b35e185 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Eustace?= Date: Fri, 17 Aug 2018 16:06:39 +0200 Subject: [PATCH 002/256] Fix missing x token for string formatting --- CHANGELOG.md | 1 + pendulum/formatting/formatter.py | 19 ++++++++++++++++++- tests/formatting/test_formatter.py | 8 ++++++++ 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e5b5fc8c..01a160bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Fixed - Fixed `from_format()` not recognizing input strings when the specified pattern had escaped elements. +- Fixed missing `x` token for string formatting. ## [2.0.3] - 2018-07-30 diff --git a/pendulum/formatting/formatter.py b/pendulum/formatting/formatter.py index 924c802e..a90a4419 100644 --- a/pendulum/formatting/formatter.py +++ b/pendulum/formatting/formatter.py @@ -37,7 +37,23 @@ class Formatter: - _TOKENS = "\[([^\[]*)\]|\\\(.)|" "(" "Mo|MM?M?M?" "|Do|DDDo|DD?D?D?|ddd?d?|do?" "|E{1,4}" "|w[o|w]?|W[o|W]?|Qo?" "|YYYY|YY|Y" "|gg(ggg?)?|GG(GGG?)?" "|a|A" "|hh?|HH?|kk?" "|mm?|ss?|S{1,9}" "|x|X" "|zz?|ZZ?" "|LTS|LT|LL?L?L?" ")" + _TOKENS = ( + "\[([^\[]*)\]|\\\(.)|" + "(" + "Mo|MM?M?M?" + "|Do|DDDo|DD?D?D?|ddd?d?|do?" + "|E{1,4}" + "|w[o|w]?|W[o|W]?|Qo?" + "|YYYY|YY|Y" + "|gg(ggg?)?|GG(GGG?)?" + "|a|A" + "|hh?|HH?|kk?" + "|mm?|ss?|S{1,9}" + "|x|X" + "|zz?|ZZ?" + "|LTS|LT|LL?L?L?" + ")" + ) _FORMAT_RE = re.compile(_TOKENS) @@ -106,6 +122,7 @@ class Formatter: "SSSSSS": lambda dt: "{:06d}".format(dt.microsecond), # Timestamp "X": lambda dt: "{:d}".format(dt.int_timestamp), + "x": lambda dt: "{:d}".format(dt.int_timestamp * 1000 + dt.microsecond // 1000), # Timezone "zz": lambda dt: "{}".format(dt.tzname() if dt.tzinfo is not None else ""), "z": lambda dt: "{}".format(dt.timezone_name or ""), diff --git a/tests/formatting/test_formatter.py b/tests/formatting/test_formatter.py index 03582ad1..40f2ee8e 100644 --- a/tests/formatting/test_formatter.py +++ b/tests/formatting/test_formatter.py @@ -209,6 +209,14 @@ def test_timestamp(): assert f.format(d.add(days=1), "X") == "86400" +def test_timestamp_milliseconds(): + f = Formatter() + d = pendulum.datetime(1970, 1, 1) + assert f.format(d, "x") == "0" + assert f.format(d.add(days=1), "x") == "86400000" + assert f.format(d.add(days=1, microseconds=129123), "x") == "86400129" + + def test_date_formats(): f = Formatter() d = pendulum.datetime(2016, 8, 28, 7, 3, 6, 123456) From 7d797e073b56b11ca784e9db6687073e5ec7f813 Mon Sep 17 00:00:00 2001 From: Arie Bovenberg Date: Sun, 9 Sep 2018 19:09:07 +0200 Subject: [PATCH 003/256] add NL locale --- pendulum/locales/nl/__init__.py | 0 pendulum/locales/nl/custom.py | 26 ++++ pendulum/locales/nl/locale.py | 215 ++++++++++++++++++++++++++++++++ tests/localization/test_nl.py | 83 ++++++++++++ 4 files changed, 324 insertions(+) create mode 100644 pendulum/locales/nl/__init__.py create mode 100644 pendulum/locales/nl/custom.py create mode 100644 pendulum/locales/nl/locale.py create mode 100644 tests/localization/test_nl.py diff --git a/pendulum/locales/nl/__init__.py b/pendulum/locales/nl/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pendulum/locales/nl/custom.py b/pendulum/locales/nl/custom.py new file mode 100644 index 00000000..e50c74db --- /dev/null +++ b/pendulum/locales/nl/custom.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +""" +nl custom locale file. +""" + +translations = { + "units": {"few_second": "enkele seconden"}, + # Relative time + "ago": "{} geleden", + "from_now": "over {}", + "after": "{0} later", + "before": "{0} eerder", + # Ordinals + "ordinal": {"other": "e"}, + # Date formats + "date_formats": { + 'L': 'DD-MM-YYYY', + 'LL': 'D MMMM YYYY', + 'LLL': 'D MMMM YYYY HH:mm', + 'LLLL': 'dddd D MMMM YYYY HH:mm', + 'LT': 'HH:mm', + 'LTS': 'HH:mm:ss', + }, +} diff --git a/pendulum/locales/nl/locale.py b/pendulum/locales/nl/locale.py new file mode 100644 index 00000000..9a8d6e59 --- /dev/null +++ b/pendulum/locales/nl/locale.py @@ -0,0 +1,215 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals +""" +nl locale file. + +It has been generated automatically and must not be modified directly. +""" + +from .custom import translations as custom_translations + + +locale = { + 'plural': lambda n: 'one' if ((n == n and ((n == 1))) and (0 == 0 and ((0 == 0)))) else 'other', + 'ordinal': lambda n: 'other', + 'translations': { + 'days': { + 'abbreviated': { + 0: 'zo', + 1: 'ma', + 2: 'di', + 3: 'wo', + 4: 'do', + 5: 'vr', + 6: 'za', + }, + 'narrow': { + 0: 'Z', + 1: 'M', + 2: 'D', + 3: 'W', + 4: 'D', + 5: 'V', + 6: 'Z', + }, + 'short': { + 0: 'zo', + 1: 'ma', + 2: 'di', + 3: 'wo', + 4: 'do', + 5: 'vr', + 6: 'za', + }, + 'wide': { + 0: 'zondag', + 1: 'maandag', + 2: 'dinsdag', + 3: 'woensdag', + 4: 'donderdag', + 5: 'vrijdag', + 6: 'zaterdag', + }, + }, + 'months': { + 'abbreviated': { + 1: 'jan.', + 2: 'feb.', + 3: 'mrt.', + 4: 'apr.', + 5: 'mei', + 6: 'jun.', + 7: 'jul.', + 8: 'aug.', + 9: 'sep.', + 10: 'okt.', + 11: 'nov.', + 12: 'dec.', + }, + 'narrow': { + 1: 'J', + 2: 'F', + 3: 'M', + 4: 'A', + 5: 'M', + 6: 'J', + 7: 'J', + 8: 'A', + 9: 'S', + 10: 'O', + 11: 'N', + 12: 'D', + }, + 'wide': { + 1: 'januari', + 2: 'februari', + 3: 'maart', + 4: 'april', + 5: 'mei', + 6: 'juni', + 7: 'juli', + 8: 'augustus', + 9: 'september', + 10: 'oktober', + 11: 'november', + 12: 'december', + }, + }, + 'units': { + 'year': { + 'one': '{0} jaar', + 'other': '{0} jaar', + }, + 'month': { + 'one': '{0} maand', + 'other': '{0} maanden', + }, + 'week': { + 'one': '{0} week', + 'other': '{0} weken', + }, + 'day': { + 'one': '{0} dag', + 'other': '{0} dagen', + }, + 'hour': { + 'one': '{0} uur', + 'other': '{0} uur', + }, + 'minute': { + 'one': '{0} minuut', + 'other': '{0} minuten', + }, + 'second': { + 'one': '{0} seconde', + 'other': '{0} seconden', + }, + 'microsecond': { + 'one': '{0} microseconde', + 'other': '{0} microseconden', + }, + }, + 'relative': { + 'year': { + 'future': { + 'other': 'over {0} jaar', + 'one': 'over {0} jaar', + }, + 'past': { + 'other': '{0} jaar geleden', + 'one': '{0} jaar geleden', + }, + }, + 'month': { + 'future': { + 'other': 'over {0} maanden', + 'one': 'over {0} maand', + }, + 'past': { + 'other': '{0} maanden geleden', + 'one': '{0} maand geleden', + }, + }, + 'week': { + 'future': { + 'other': 'over {0} weken', + 'one': 'over {0} week', + }, + 'past': { + 'other': '{0} weken geleden', + 'one': '{0} week geleden', + }, + }, + 'day': { + 'future': { + 'other': 'over {0} dagen', + 'one': 'over {0} dag', + }, + 'past': { + 'other': '{0} dagen geleden', + 'one': '{0} dag geleden', + }, + }, + 'hour': { + 'future': { + 'other': 'over {0} uur', + 'one': 'over {0} uur', + }, + 'past': { + 'other': '{0} uur geleden', + 'one': '{0} uur geleden', + }, + }, + 'minute': { + 'future': { + 'other': 'over {0} minuten', + 'one': 'over {0} minuut', + }, + 'past': { + 'other': '{0} minuten geleden', + 'one': '{0} minuut geleden', + }, + }, + 'second': { + 'future': { + 'other': 'over {0} seconden', + 'one': 'over {0} seconde', + }, + 'past': { + 'other': '{0} seconden geleden', + 'one': '{0} seconde geleden', + }, + }, + }, + 'day_periods': { + 'midnight': 'middernacht', + 'am': 'a.m.', + 'pm': 'p.m.', + 'morning1': '‘s ochtends', + 'afternoon1': '‘s middags', + 'evening1': '‘s avonds', + 'night1': '‘s nachts', + }, + }, + 'custom': custom_translations +} diff --git a/tests/localization/test_nl.py b/tests/localization/test_nl.py new file mode 100644 index 00000000..62801a63 --- /dev/null +++ b/tests/localization/test_nl.py @@ -0,0 +1,83 @@ + +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +import pendulum + +locale = "nl" + + +def test_diff_for_humans(): + with pendulum.test(pendulum.datetime(2016, 8, 29)): + diff_for_humans() + + +def diff_for_humans(): + d = pendulum.now().subtract(seconds=1) + assert d.diff_for_humans(locale=locale) == "enkele seconden geleden" + + d = pendulum.now().subtract(seconds=2) + assert d.diff_for_humans(locale=locale) == "enkele seconden geleden" + + d = pendulum.now().subtract(seconds=22) + assert d.diff_for_humans(locale=locale) == "22 seconden geleden" + + d = pendulum.now().subtract(minutes=1) + assert d.diff_for_humans(locale=locale) == "1 minuut geleden" + + d = pendulum.now().subtract(minutes=2) + assert d.diff_for_humans(locale=locale) == "2 minuten geleden" + + d = pendulum.now().subtract(hours=1) + assert d.diff_for_humans(locale=locale) == "1 uur geleden" + + d = pendulum.now().subtract(hours=2) + assert d.diff_for_humans(locale=locale) == "2 uur geleden" + + d = pendulum.now().subtract(days=1) + assert d.diff_for_humans(locale=locale) == "1 dag geleden" + + d = pendulum.now().subtract(days=2) + assert d.diff_for_humans(locale=locale) == "2 dagen geleden" + + d = pendulum.now().subtract(weeks=1) + assert d.diff_for_humans(locale=locale) == "1 week geleden" + + d = pendulum.now().subtract(weeks=2) + assert d.diff_for_humans(locale=locale) == "2 weken geleden" + + d = pendulum.now().subtract(months=1) + assert d.diff_for_humans(locale=locale) == "1 maand geleden" + + d = pendulum.now().subtract(months=2) + assert d.diff_for_humans(locale=locale) == "2 maanden geleden" + + d = pendulum.now().subtract(years=1) + assert d.diff_for_humans(locale=locale) == "1 jaar geleden" + + d = pendulum.now().subtract(years=2) + assert d.diff_for_humans(locale=locale) == "2 jaar geleden" + + d = pendulum.now().add(seconds=1) + assert d.diff_for_humans(locale=locale) == "over enkele seconden" + + d = pendulum.now().add(weeks=1) + assert d.diff_for_humans(locale=locale) == "over 1 week" + + d = pendulum.now().add(seconds=1) + d2 = pendulum.now() + assert d.diff_for_humans(d2, locale=locale) == "enkele seconden later" + assert d2.diff_for_humans(d, locale=locale) == "enkele seconden eerder" + + assert d.diff_for_humans(d2, True, locale=locale) == "enkele seconden" + assert d2.diff_for_humans(d.add(seconds=1), True, locale=locale) == "enkele seconden" + + +def test_format(): + d = pendulum.datetime(2016, 8, 28, 7, 3, 6, 123456) + assert d.format("dddd", locale=locale) == "zondag" + assert d.format("ddd", locale=locale) == "zo" + assert d.format("MMMM", locale=locale) == "augustus" + assert d.format("MMM", locale=locale) == "aug." + assert d.format("A", locale=locale) == "a.m." + assert d.format("Do", locale=locale) == "28e" From 626962b9935dd10ba4c188c5dd17f183312b21a1 Mon Sep 17 00:00:00 2001 From: Arie Bovenberg Date: Sun, 9 Sep 2018 19:16:28 +0200 Subject: [PATCH 004/256] apply "black" code style --- pendulum/locales/nl/custom.py | 12 +- pendulum/locales/nl/locale.py | 295 +++++++++++++--------------------- tests/localization/test_nl.py | 4 +- 3 files changed, 117 insertions(+), 194 deletions(-) diff --git a/pendulum/locales/nl/custom.py b/pendulum/locales/nl/custom.py index e50c74db..e1123728 100644 --- a/pendulum/locales/nl/custom.py +++ b/pendulum/locales/nl/custom.py @@ -16,11 +16,11 @@ "ordinal": {"other": "e"}, # Date formats "date_formats": { - 'L': 'DD-MM-YYYY', - 'LL': 'D MMMM YYYY', - 'LLL': 'D MMMM YYYY HH:mm', - 'LLLL': 'dddd D MMMM YYYY HH:mm', - 'LT': 'HH:mm', - 'LTS': 'HH:mm:ss', + "L": "DD-MM-YYYY", + "LL": "D MMMM YYYY", + "LLL": "D MMMM YYYY HH:mm", + "LLLL": "dddd D MMMM YYYY HH:mm", + "LT": "HH:mm", + "LTS": "HH:mm:ss", }, } diff --git a/pendulum/locales/nl/locale.py b/pendulum/locales/nl/locale.py index 9a8d6e59..d0a6324a 100644 --- a/pendulum/locales/nl/locale.py +++ b/pendulum/locales/nl/locale.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals + """ nl locale file. @@ -10,206 +11,126 @@ locale = { - 'plural': lambda n: 'one' if ((n == n and ((n == 1))) and (0 == 0 and ((0 == 0)))) else 'other', - 'ordinal': lambda n: 'other', - 'translations': { - 'days': { - 'abbreviated': { - 0: 'zo', - 1: 'ma', - 2: 'di', - 3: 'wo', - 4: 'do', - 5: 'vr', - 6: 'za', - }, - 'narrow': { - 0: 'Z', - 1: 'M', - 2: 'D', - 3: 'W', - 4: 'D', - 5: 'V', - 6: 'Z', - }, - 'short': { - 0: 'zo', - 1: 'ma', - 2: 'di', - 3: 'wo', - 4: 'do', - 5: 'vr', - 6: 'za', - }, - 'wide': { - 0: 'zondag', - 1: 'maandag', - 2: 'dinsdag', - 3: 'woensdag', - 4: 'donderdag', - 5: 'vrijdag', - 6: 'zaterdag', + "plural": lambda n: "one" + if ((n == n and ((n == 1))) and (0 == 0 and ((0 == 0)))) + else "other", + "ordinal": lambda n: "other", + "translations": { + "days": { + "abbreviated": { + 0: "zo", + 1: "ma", + 2: "di", + 3: "wo", + 4: "do", + 5: "vr", + 6: "za", + }, + "narrow": {0: "Z", 1: "M", 2: "D", 3: "W", 4: "D", 5: "V", 6: "Z"}, + "short": {0: "zo", 1: "ma", 2: "di", 3: "wo", 4: "do", 5: "vr", 6: "za"}, + "wide": { + 0: "zondag", + 1: "maandag", + 2: "dinsdag", + 3: "woensdag", + 4: "donderdag", + 5: "vrijdag", + 6: "zaterdag", }, }, - 'months': { - 'abbreviated': { - 1: 'jan.', - 2: 'feb.', - 3: 'mrt.', - 4: 'apr.', - 5: 'mei', - 6: 'jun.', - 7: 'jul.', - 8: 'aug.', - 9: 'sep.', - 10: 'okt.', - 11: 'nov.', - 12: 'dec.', - }, - 'narrow': { - 1: 'J', - 2: 'F', - 3: 'M', - 4: 'A', - 5: 'M', - 6: 'J', - 7: 'J', - 8: 'A', - 9: 'S', - 10: 'O', - 11: 'N', - 12: 'D', - }, - 'wide': { - 1: 'januari', - 2: 'februari', - 3: 'maart', - 4: 'april', - 5: 'mei', - 6: 'juni', - 7: 'juli', - 8: 'augustus', - 9: 'september', - 10: 'oktober', - 11: 'november', - 12: 'december', + "months": { + "abbreviated": { + 1: "jan.", + 2: "feb.", + 3: "mrt.", + 4: "apr.", + 5: "mei", + 6: "jun.", + 7: "jul.", + 8: "aug.", + 9: "sep.", + 10: "okt.", + 11: "nov.", + 12: "dec.", + }, + "narrow": { + 1: "J", + 2: "F", + 3: "M", + 4: "A", + 5: "M", + 6: "J", + 7: "J", + 8: "A", + 9: "S", + 10: "O", + 11: "N", + 12: "D", + }, + "wide": { + 1: "januari", + 2: "februari", + 3: "maart", + 4: "april", + 5: "mei", + 6: "juni", + 7: "juli", + 8: "augustus", + 9: "september", + 10: "oktober", + 11: "november", + 12: "december", }, }, - 'units': { - 'year': { - 'one': '{0} jaar', - 'other': '{0} jaar', - }, - 'month': { - 'one': '{0} maand', - 'other': '{0} maanden', - }, - 'week': { - 'one': '{0} week', - 'other': '{0} weken', - }, - 'day': { - 'one': '{0} dag', - 'other': '{0} dagen', - }, - 'hour': { - 'one': '{0} uur', - 'other': '{0} uur', - }, - 'minute': { - 'one': '{0} minuut', - 'other': '{0} minuten', - }, - 'second': { - 'one': '{0} seconde', - 'other': '{0} seconden', - }, - 'microsecond': { - 'one': '{0} microseconde', - 'other': '{0} microseconden', - }, + "units": { + "year": {"one": "{0} jaar", "other": "{0} jaar"}, + "month": {"one": "{0} maand", "other": "{0} maanden"}, + "week": {"one": "{0} week", "other": "{0} weken"}, + "day": {"one": "{0} dag", "other": "{0} dagen"}, + "hour": {"one": "{0} uur", "other": "{0} uur"}, + "minute": {"one": "{0} minuut", "other": "{0} minuten"}, + "second": {"one": "{0} seconde", "other": "{0} seconden"}, + "microsecond": {"one": "{0} microseconde", "other": "{0} microseconden"}, }, - 'relative': { - 'year': { - 'future': { - 'other': 'over {0} jaar', - 'one': 'over {0} jaar', - }, - 'past': { - 'other': '{0} jaar geleden', - 'one': '{0} jaar geleden', - }, + "relative": { + "year": { + "future": {"other": "over {0} jaar", "one": "over {0} jaar"}, + "past": {"other": "{0} jaar geleden", "one": "{0} jaar geleden"}, }, - 'month': { - 'future': { - 'other': 'over {0} maanden', - 'one': 'over {0} maand', - }, - 'past': { - 'other': '{0} maanden geleden', - 'one': '{0} maand geleden', - }, + "month": { + "future": {"other": "over {0} maanden", "one": "over {0} maand"}, + "past": {"other": "{0} maanden geleden", "one": "{0} maand geleden"}, }, - 'week': { - 'future': { - 'other': 'over {0} weken', - 'one': 'over {0} week', - }, - 'past': { - 'other': '{0} weken geleden', - 'one': '{0} week geleden', - }, + "week": { + "future": {"other": "over {0} weken", "one": "over {0} week"}, + "past": {"other": "{0} weken geleden", "one": "{0} week geleden"}, }, - 'day': { - 'future': { - 'other': 'over {0} dagen', - 'one': 'over {0} dag', - }, - 'past': { - 'other': '{0} dagen geleden', - 'one': '{0} dag geleden', - }, + "day": { + "future": {"other": "over {0} dagen", "one": "over {0} dag"}, + "past": {"other": "{0} dagen geleden", "one": "{0} dag geleden"}, }, - 'hour': { - 'future': { - 'other': 'over {0} uur', - 'one': 'over {0} uur', - }, - 'past': { - 'other': '{0} uur geleden', - 'one': '{0} uur geleden', - }, + "hour": { + "future": {"other": "over {0} uur", "one": "over {0} uur"}, + "past": {"other": "{0} uur geleden", "one": "{0} uur geleden"}, }, - 'minute': { - 'future': { - 'other': 'over {0} minuten', - 'one': 'over {0} minuut', - }, - 'past': { - 'other': '{0} minuten geleden', - 'one': '{0} minuut geleden', - }, + "minute": { + "future": {"other": "over {0} minuten", "one": "over {0} minuut"}, + "past": {"other": "{0} minuten geleden", "one": "{0} minuut geleden"}, }, - 'second': { - 'future': { - 'other': 'over {0} seconden', - 'one': 'over {0} seconde', - }, - 'past': { - 'other': '{0} seconden geleden', - 'one': '{0} seconde geleden', - }, + "second": { + "future": {"other": "over {0} seconden", "one": "over {0} seconde"}, + "past": {"other": "{0} seconden geleden", "one": "{0} seconde geleden"}, }, }, - 'day_periods': { - 'midnight': 'middernacht', - 'am': 'a.m.', - 'pm': 'p.m.', - 'morning1': '‘s ochtends', - 'afternoon1': '‘s middags', - 'evening1': '‘s avonds', - 'night1': '‘s nachts', + "day_periods": { + "midnight": "middernacht", + "am": "a.m.", + "pm": "p.m.", + "morning1": "‘s ochtends", + "afternoon1": "‘s middags", + "evening1": "‘s avonds", + "night1": "‘s nachts", }, }, - 'custom': custom_translations + "custom": custom_translations, } diff --git a/tests/localization/test_nl.py b/tests/localization/test_nl.py index 62801a63..5e92b2a1 100644 --- a/tests/localization/test_nl.py +++ b/tests/localization/test_nl.py @@ -70,7 +70,9 @@ def diff_for_humans(): assert d2.diff_for_humans(d, locale=locale) == "enkele seconden eerder" assert d.diff_for_humans(d2, True, locale=locale) == "enkele seconden" - assert d2.diff_for_humans(d.add(seconds=1), True, locale=locale) == "enkele seconden" + assert ( + d2.diff_for_humans(d.add(seconds=1), True, locale=locale) == "enkele seconden" + ) def test_format(): From bb058f6b78b2d28ef5d9a5e759cfa179a1a713d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Eustace?= Date: Fri, 14 Sep 2018 09:35:32 -0500 Subject: [PATCH 005/256] Use the latest installer for Poetry in CI --- .travis.yml | 2 +- appveyor.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index e0685ac5..ed882129 100644 --- a/.travis.yml +++ b/.travis.yml @@ -63,7 +63,7 @@ install: virtualenv --python="$PYENV_ROOT/versions/pypy-$PYPY_VERSION/bin/python" "$HOME/virtualenvs/pypy-$PYPY_VERSION" source "$HOME/virtualenvs/pypy-$PYPY_VERSION/bin/activate" fi - - wget https://raw.githubusercontent.com/sdispater/poetry/master/get-poetry.py + - wget https://raw.githubusercontent.com/sdispater/poetry/develop/get-poetry.py - python get-poetry.py --preview - poetry install -v - poetry build -v diff --git a/appveyor.yml b/appveyor.yml index 55cba3cc..82469436 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -20,7 +20,7 @@ install: - "python -m pip install --disable-pip-version-check --user --upgrade pip" # Downloading and installing poetry - - curl -fsS -o get-poetry.py https://raw.githubusercontent.com/sdispater/poetry/master/get-poetry.py + - curl -fsS -o get-poetry.py https://raw.githubusercontent.com/sdispater/poetry/develop/get-poetry.py - python get-poetry.py --preview # Install dependencies From 976cd5bbf62f9181077962b184a33d1128267395 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Eustace?= Date: Fri, 14 Sep 2018 09:41:44 -0500 Subject: [PATCH 006/256] Update CI configuration --- .travis.yml | 2 +- appveyor.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index ed882129..fd30a224 100644 --- a/.travis.yml +++ b/.travis.yml @@ -64,7 +64,7 @@ install: source "$HOME/virtualenvs/pypy-$PYPY_VERSION/bin/activate" fi - wget https://raw.githubusercontent.com/sdispater/poetry/develop/get-poetry.py - - python get-poetry.py --preview + - python get-poetry.py --preview -y - poetry install -v - poetry build -v - | diff --git a/appveyor.yml b/appveyor.yml index 82469436..98453301 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -21,7 +21,7 @@ install: # Downloading and installing poetry - curl -fsS -o get-poetry.py https://raw.githubusercontent.com/sdispater/poetry/develop/get-poetry.py - - python get-poetry.py --preview + - python get-poetry.py --preview -y # Install dependencies - python -m pip install codecov From c98d8bc018a8c1723b94949a929d8a60f99ed9fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Eustace?= Date: Fri, 14 Sep 2018 10:42:53 -0500 Subject: [PATCH 007/256] Update CI configuration --- .travis.yml | 3 +-- appveyor.yml | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index fd30a224..33f98be1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -63,8 +63,7 @@ install: virtualenv --python="$PYENV_ROOT/versions/pypy-$PYPY_VERSION/bin/python" "$HOME/virtualenvs/pypy-$PYPY_VERSION" source "$HOME/virtualenvs/pypy-$PYPY_VERSION/bin/activate" fi - - wget https://raw.githubusercontent.com/sdispater/poetry/develop/get-poetry.py - - python get-poetry.py --preview -y + - pip install poetry --pre -U - poetry install -v - poetry build -v - | diff --git a/appveyor.yml b/appveyor.yml index 98453301..a357f09e 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -20,8 +20,7 @@ install: - "python -m pip install --disable-pip-version-check --user --upgrade pip" # Downloading and installing poetry - - curl -fsS -o get-poetry.py https://raw.githubusercontent.com/sdispater/poetry/develop/get-poetry.py - - python get-poetry.py --preview -y + - python -m pip install poetry --pre -U # Install dependencies - python -m pip install codecov From ea1d8fbfecfcdee56cb6baa48521dee83607bde4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Eustace?= Date: Fri, 14 Sep 2018 17:38:10 -0500 Subject: [PATCH 008/256] Update CI configuration --- .travis.yml | 2 +- appveyor.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 33f98be1..502d9a18 100644 --- a/.travis.yml +++ b/.travis.yml @@ -63,7 +63,7 @@ install: virtualenv --python="$PYENV_ROOT/versions/pypy-$PYPY_VERSION/bin/python" "$HOME/virtualenvs/pypy-$PYPY_VERSION" source "$HOME/virtualenvs/pypy-$PYPY_VERSION/bin/activate" fi - - pip install poetry --pre -U + - pip install poetry -U - poetry install -v - poetry build -v - | diff --git a/appveyor.yml b/appveyor.yml index a357f09e..ba7c0c56 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -20,7 +20,7 @@ install: - "python -m pip install --disable-pip-version-check --user --upgrade pip" # Downloading and installing poetry - - python -m pip install poetry --pre -U + - python -m pip install poetry -U # Install dependencies - python -m pip install codecov From f32d2d1c2ffb1414262e9472f201af42a1a8be39 Mon Sep 17 00:00:00 2001 From: Tasos Stamadianos Date: Mon, 8 Oct 2018 23:55:20 -0400 Subject: [PATCH 009/256] Added support for parsing padded 2-digit days of the month with 'from_format' --- CHANGELOG.md | 1 + pendulum/formatting/formatter.py | 3 ++- tests/datetime/test_from_format.py | 10 ++++++++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 01a160bd..ea2d6ea0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - Fixed `from_format()` not recognizing input strings when the specified pattern had escaped elements. - Fixed missing `x` token for string formatting. +- Added support for parsing padded 2-digit days of the month with `from_format()` ## [2.0.3] - 2018-07-30 diff --git a/pendulum/formatting/formatter.py b/pendulum/formatting/formatter.py index a90a4419..c5828d33 100644 --- a/pendulum/formatting/formatter.py +++ b/pendulum/formatting/formatter.py @@ -17,6 +17,7 @@ _MATCH_4 = "\d{4}" _MATCH_6 = "[+-]?\d{6}" _MATCH_1_TO_2 = "\d\d?" +_MATCH_1_TO_2_LEFT_PAD = "[0-9 ]\d?" _MATCH_1_TO_3 = "\d{1,3}" _MATCH_1_TO_4 = "\d{1,4}" _MATCH_1_TO_6 = "[+-]?\d{1,6}" @@ -157,7 +158,7 @@ class Formatter: "MMM": _MATCH_WORD, "MMMM": _MATCH_WORD, "D": _MATCH_1_TO_2, - "DD": (_MATCH_1_TO_2, _MATCH_2), + "DD": (_MATCH_1_TO_2_LEFT_PAD, _MATCH_2), "DDD": _MATCH_1_TO_3, "DDDD": _MATCH_3, "dddd": _MATCH_WORD, diff --git a/tests/datetime/test_from_format.py b/tests/datetime/test_from_format.py index 480e2cf6..e167a99c 100644 --- a/tests/datetime/test_from_format.py +++ b/tests/datetime/test_from_format.py @@ -42,6 +42,16 @@ def test_from_format_with_millis(): assert_datetime(d, 1975, 5, 21, 22, 32, 11, 123456) +def test_from_format_with_padded_day(): + d = pendulum.from_format("Apr 2 12:00:00 2020 GMT", "MMM DD HH:mm:ss YYYY z") + assert_datetime(d, 2020, 4, 2, 12) + + +def test_from_format_with_invalid_padded_day(): + with pytest.raises(ValueError): + d = pendulum.from_format("Apr 2 12:00:00 2020 GMT", "MMM DD HH:mm:ss YYYY z") + + @pytest.mark.parametrize( "text,fmt,expected,now", [ From a111349fd7c19ff5e9b3ebbce42401660a868526 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Eustace?= Date: Thu, 18 Oct 2018 16:01:50 -0500 Subject: [PATCH 010/256] Update files to use Poetry >=0.12.0 --- .gitignore | 3 ++- .travis.yml | 4 +++- pyproject.toml | 5 +++++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index f1152e6f..87088474 100644 --- a/.gitignore +++ b/.gitignore @@ -31,7 +31,8 @@ profile.html /wheelhouse /docs/site/* pyproject.lock +/poetry.lock # editor -.vscode \ No newline at end of file +.vscode diff --git a/.travis.yml b/.travis.yml index 502d9a18..eeb9600a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -63,7 +63,9 @@ install: virtualenv --python="$PYENV_ROOT/versions/pypy-$PYPY_VERSION/bin/python" "$HOME/virtualenvs/pypy-$PYPY_VERSION" source "$HOME/virtualenvs/pypy-$PYPY_VERSION/bin/activate" fi - - pip install poetry -U + - pip install pip -U + - curl -sSL https://raw.githubusercontent.com/sdispater/poetry/master/get-poetry.py | python + - source $HOME/.poetry/env - poetry install -v - poetry build -v - | diff --git a/pyproject.toml b/pyproject.toml index 6042fa50..14ae6f5b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,3 +33,8 @@ cleo = "^0.6.1" tox = "^3.0" black = { version = "^18.4-alpha.0", python = "^3.6" } pre-commit = "^1.10" + + +[build-system] +requires = ["poetry>=0.12"] +build-backend = "poetry.masonry.api" From 0e91f165a4ac6ef68d7e1119d1269d3fea801a8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Eustace?= Date: Mon, 29 Oct 2018 10:38:05 -0500 Subject: [PATCH 011/256] Fix reading timezone files --- CHANGELOG.md | 1 + pendulum/tz/zoneinfo/reader.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 01a160bd..cbe3425f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - Fixed `from_format()` not recognizing input strings when the specified pattern had escaped elements. - Fixed missing `x` token for string formatting. +- Fixed reading timezone files. ## [2.0.3] - 2018-07-30 diff --git a/pendulum/tz/zoneinfo/reader.py b/pendulum/tz/zoneinfo/reader.py index 34457c20..77fd0454 100644 --- a/pendulum/tz/zoneinfo/reader.py +++ b/pendulum/tz/zoneinfo/reader.py @@ -64,7 +64,7 @@ def _check_read(self, fd, nbytes): # type: (...) -> bytes """ result = fd.read(nbytes) - if not result or len(result) != nbytes: + if (not result and nbytes > 0) or len(result) != nbytes: raise InvalidZoneinfoFile( "Expected {} bytes reading {}, " "but got {}".format(nbytes, fd.name, len(result) if result else 0) From ea921deb870e417468ab6c04245d24f4b9d53dd2 Mon Sep 17 00:00:00 2001 From: Andrew Cordery Date: Mon, 29 Oct 2018 10:04:10 -0600 Subject: [PATCH 012/256] Corrected 'z' timezone parsing in from_format to allow underscores (#268) Ex: America/New_York. Expanded tests to ensure parsing of all symbols found in pendulum.timezones. --- pendulum/formatting/formatter.py | 2 +- tests/datetime/test_from_format.py | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/pendulum/formatting/formatter.py b/pendulum/formatting/formatter.py index a90a4419..6c0e759a 100644 --- a/pendulum/formatting/formatter.py +++ b/pendulum/formatting/formatter.py @@ -32,7 +32,7 @@ "['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+" "|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}" ) -_MATCH_TIMEZONE = "[A-za-z0-9-+]+(/[A-Za-z0-9-+]+)?" +_MATCH_TIMEZONE = "[A-za-z0-9-+]+(/[A-Za-z0-9-+_]+)?" class Formatter: diff --git a/tests/datetime/test_from_format.py b/tests/datetime/test_from_format.py index 480e2cf6..b27abb7a 100644 --- a/tests/datetime/test_from_format.py +++ b/tests/datetime/test_from_format.py @@ -92,6 +92,30 @@ def test_from_format_with_millis(): "1975-12-25T14:15:16-05:00", None, ), + ( + "1975-12-25T14:15:16 America/New_York", + "YYYY-MM-DDTHH:mm:ss z", + "1975-12-25T14:15:16-05:00", + None, + ), + ( + "1975-12-25T14:15:16 Africa/Porto-Novo", + "YYYY-MM-DDTHH:mm:ss z", + "1975-12-25T14:15:16+01:00", + None, + ), + ( + "1975-12-25T14:15:16 Etc/GMT+0", + "YYYY-MM-DDTHH:mm:ss z", + "1975-12-25T14:15:16+00:00", + None, + ), + ( + "1975-12-25T14:15:16 W-SU", + "YYYY-MM-DDTHH:mm:ss z", + "1975-12-25T14:15:16+03:00", + None, + ), ], ) def test_from_format(text, fmt, expected, now): From 033f675a6f21ceded2082ad7328ada94444fc0ba Mon Sep 17 00:00:00 2001 From: cgohlke Date: Mon, 29 Oct 2018 09:05:24 -0700 Subject: [PATCH 013/256] Fix Visual Studio build errors (#261) * Fix iso8601.c(220): error C2090: function returns array * Fix error LNK2001: unresolved external symbol p --- pendulum/_extensions/_helpers.c | 2 +- pendulum/parsing/_iso8601.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pendulum/_extensions/_helpers.c b/pendulum/_extensions/_helpers.c index 2b744614..e3e3253c 100644 --- a/pendulum/_extensions/_helpers.c +++ b/pendulum/_extensions/_helpers.c @@ -102,7 +102,7 @@ int _is_leap(int year) { } int _is_long_year(int year) { - return (p(year) % 7 == 4) || (p(year - 1) % 7 == 3); + return (_p(year) % 7 == 4) || (_p(year - 1) % 7 == 3); } int _week_day(int year, int month, int day) { diff --git a/pendulum/parsing/_iso8601.c b/pendulum/parsing/_iso8601.c index 1387332c..a81f25aa 100644 --- a/pendulum/parsing/_iso8601.c +++ b/pendulum/parsing/_iso8601.c @@ -217,7 +217,7 @@ static PyObject *FixedOffset_dst(FixedOffset *self, PyObject *args) { * return "%s%d:%d" % (sign, self.offset / 60, self.offset % 60) */ static PyObject *FixedOffset_tzname(FixedOffset *self, PyObject *args) { - char tzname[7] = {0}; + char tzname_[7] = {0}; char sign = '+'; int offset = self->offset; @@ -227,14 +227,14 @@ static PyObject *FixedOffset_tzname(FixedOffset *self, PyObject *args) { } sprintf( - tzname, + tzname_, "%c%02d:%02d", sign, offset / SECS_PER_HOUR, offset / SECS_PER_MIN % SECS_PER_MIN ); - return PyUnicode_FromString(tzname); + return PyUnicode_FromString(tzname_); } /* From 3fca216c49851cf820bae26da6fa8bc4b74cba6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Eustace?= Date: Mon, 29 Oct 2018 11:36:12 -0500 Subject: [PATCH 014/256] Update Travis config --- .travis.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index eeb9600a..3ceaff15 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,9 +28,11 @@ jobs: env: PENDULUM_EXTENSIONS=1 - python: 3.6 env: PENDULUM_EXTENSIONS=0 - - python: 3.7-dev + - python: 3.7 + dist: xenial env: PENDULUM_EXTENSIONS=1 - - python: 3.7-dev + - python: 3.7 + dist: xenial env: PENDULUM_EXTENSIONS=0 - python: pypy - python: pypy3 From 0e66bcbef9e069dc8ccb21ea3d98dcbb3a4b4ff8 Mon Sep 17 00:00:00 2001 From: Aleksey Kutepov Date: Mon, 29 Oct 2018 20:10:10 +0300 Subject: [PATCH 015/256] Incorrect age calculation for future dates (#291) --- pendulum/date.py | 2 +- pendulum/datetime.py | 2 +- tests/date/test_getters.py | 3 ++- tests/datetime/test_getters.py | 3 ++- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/pendulum/date.py b/pendulum/date.py index eecf4292..2d88bc87 100644 --- a/pendulum/date.py +++ b/pendulum/date.py @@ -80,7 +80,7 @@ def week_of_month(self): @property def age(self): - return self.diff().in_years() + return self.diff(abs=False).in_years() @property def quarter(self): diff --git a/pendulum/datetime.py b/pendulum/datetime.py index 59ebcfb0..d1f82eee 100644 --- a/pendulum/datetime.py +++ b/pendulum/datetime.py @@ -230,7 +230,7 @@ def timezone_name(self): # type: () -> Union[str, None] @property def age(self): - return self.date().diff(self.now(self.tz).date()).in_years() + return self.date().diff(self.now(self.tz).date(), abs=False).in_years() def is_local(self): return self.offset == self.in_timezone(pendulum.local_timezone()).offset diff --git a/tests/date/test_getters.py b/tests/date/test_getters.py index 2e4167b6..eda58447 100644 --- a/tests/date/test_getters.py +++ b/tests/date/test_getters.py @@ -36,7 +36,8 @@ def test_days_in_month(): def test_age(): d = pendulum.Date.today() assert d.age == 0 - assert d.add(years=1).age == 1 + assert d.add(years=1).age == -1 + assert d.subtract(years=1).age == 1 def test_is_leap_year(): diff --git a/tests/datetime/test_getters.py b/tests/datetime/test_getters.py index 90bc671d..95297da4 100644 --- a/tests/datetime/test_getters.py +++ b/tests/datetime/test_getters.py @@ -95,7 +95,8 @@ def test_int_timestamp_accuracy(): def test_age(): d = pendulum.now() assert d.age == 0 - assert d.add(years=1).age == 1 + assert d.add(years=1).age == -1 + assert d.subtract(years=1).age == 1 def test_local(): From cc1c642c13b3d1b99e450b566bbc5684cd4c6a21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Eustace?= Date: Mon, 29 Oct 2018 12:49:28 -0500 Subject: [PATCH 016/256] Fix from_format() trying to parse escaped tokens --- CHANGELOG.md | 1 + pendulum/formatting/formatter.py | 6 ++++-- tests/datetime/test_from_format.py | 6 ++++++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d0925bf..c9fe0dda 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - Fixed missing `x` token for string formatting. - Fixed reading timezone files. - Added support for parsing padded 2-digit days of the month with `from_format()` +- Fixed `from_format()` trying to parse escaped tokens. ## [2.0.3] - 2018-07-30 diff --git a/pendulum/formatting/formatter.py b/pendulum/formatting/formatter.py index 548245a2..712e65df 100644 --- a/pendulum/formatting/formatter.py +++ b/pendulum/formatting/formatter.py @@ -58,6 +58,8 @@ class Formatter: _FORMAT_RE = re.compile(_TOKENS) + _FROM_FORMAT_RE = re.compile(r"(? Date: Mon, 29 Oct 2018 13:29:27 -0500 Subject: [PATCH 017/256] Update CHANGELOG --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c9fe0dda..518f7578 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,9 @@ - Fixed reading timezone files. - Added support for parsing padded 2-digit days of the month with `from_format()` - Fixed `from_format()` trying to parse escaped tokens. +- Fixed the `z` token timezone parsing in `from_format()` to allow underscores. +- Fixed C extensions build errors. +- Fixed `age` calculation for future dates. ## [2.0.3] - 2018-07-30 From d32f5df35aee3a8afa184b9716fc967d57cff4fd Mon Sep 17 00:00:00 2001 From: Grzegorz Redlicki Date: Mon, 29 Oct 2018 19:38:40 +0100 Subject: [PATCH 018/256] Implement recognition of the transition for int_timestamp method (#296) --- pendulum/datetime.py | 7 ++++++- tests/datetime/test_getters.py | 24 ++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/pendulum/datetime.py b/pendulum/datetime.py index d1f82eee..abca24a8 100644 --- a/pendulum/datetime.py +++ b/pendulum/datetime.py @@ -185,6 +185,11 @@ def float_timestamp(self): def int_timestamp(self): # 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, @@ -193,7 +198,7 @@ def int_timestamp(self): self.minute, self.second, self.microsecond, - tzinfo=self.tzinfo, + **kwargs ) delta = dt - self._EPOCH diff --git a/tests/datetime/test_getters.py b/tests/datetime/test_getters.py index 95297da4..3be480b0 100644 --- a/tests/datetime/test_getters.py +++ b/tests/datetime/test_getters.py @@ -4,6 +4,7 @@ from pendulum import DateTime from pendulum.tz import timezone +from pendulum.utils._compat import _HAS_FOLD from ..conftest import assert_date, assert_time @@ -92,6 +93,29 @@ def test_int_timestamp_accuracy(): assert d.int_timestamp == 32527311790 +def test_timestamp_with_transition(): + d_pre = pendulum.datetime( + 2012, 10, 28, 2, 0, tz="Europe/Warsaw", dst_rule=pendulum.PRE_TRANSITION + ) + d_post = pendulum.datetime( + 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 + + def test_age(): d = pendulum.now() assert d.age == 0 From c35c24c5ecbdb8364c59676ba74d18954585a7ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Eustace?= Date: Mon, 29 Oct 2018 20:41:39 -0500 Subject: [PATCH 019/256] Fix C extensions for Windows --- pendulum/_extensions/_helpers.c | 6 +----- pendulum/helpers.py | 5 +++++ 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/pendulum/_extensions/_helpers.c b/pendulum/_extensions/_helpers.c index e3e3253c..57ad96bb 100644 --- a/pendulum/_extensions/_helpers.c +++ b/pendulum/_extensions/_helpers.c @@ -9,10 +9,6 @@ #include #include -#ifndef PyVarObject_HEAD_INIT - #define PyVarObject_HEAD_INIT(type, size) PyObject_HEAD_INIT(type) size, -#endif - /* ------------------------------------------------------------------------- */ #define EPOCH_YEAR 1970 @@ -422,7 +418,7 @@ PyObject* timestamp(PyObject *self, PyObject *args) { result *= 60; result += second; - return PyLong_FromLong(result); + return PyLong_FromSsize_t(result); } PyObject* local_time(PyObject *self, PyObject *args) { diff --git a/pendulum/helpers.py b/pendulum/helpers.py index d69f3c86..2d6c9e37 100644 --- a/pendulum/helpers.py +++ b/pendulum/helpers.py @@ -1,14 +1,19 @@ from __future__ import absolute_import import pendulum +import os from math import copysign from datetime import datetime, date, timedelta from contextlib import contextmanager from typing import Union +with_extensions = os.getenv("PENDULUM_EXTENSIONS", "1") == "1" try: + if not with_extensions: + raise ImportError() + from ._extensions._helpers import ( local_time, precise_diff, From c00ac0ad16c0591272a361d63ee523c851eb90cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Eustace?= Date: Tue, 30 Oct 2018 12:05:35 -0500 Subject: [PATCH 020/256] Disbale C extensions for 32 bits systems --- .gitignore | 1 + Makefile | 2 +- pendulum/helpers.py | 3 ++- pendulum/parsing/__init__.py | 9 ++++++++- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 87088474..dcd8ed7d 100644 --- a/.gitignore +++ b/.gitignore @@ -32,6 +32,7 @@ profile.html /docs/site/* pyproject.lock /poetry.lock +setup.py # editor diff --git a/Makefile b/Makefile index a3a3ef44..0540be28 100644 --- a/Makefile +++ b/Makefile @@ -23,7 +23,7 @@ setup: setup-python test: @py.test --cov=pendulum --cov-config .coveragerc tests/ -sq -release: wheels_x64 cp_wheels_x64 wheels_i686 cp_wheels_i686 wheel +release: wheels_x64 cp_wheels_x64 wheel publish: @poetry publish --no-build diff --git a/pendulum/helpers.py b/pendulum/helpers.py index 2d6c9e37..5085a38b 100644 --- a/pendulum/helpers.py +++ b/pendulum/helpers.py @@ -2,6 +2,7 @@ import pendulum import os +import struct from math import copysign from datetime import datetime, date, timedelta @@ -11,7 +12,7 @@ with_extensions = os.getenv("PENDULUM_EXTENSIONS", "1") == "1" try: - if not with_extensions: + if not with_extensions or struct.calcsize("P") == 4: raise ImportError() from ._extensions._helpers import ( diff --git a/pendulum/parsing/__init__.py b/pendulum/parsing/__init__.py index 58f60a99..eef430d6 100644 --- a/pendulum/parsing/__init__.py +++ b/pendulum/parsing/__init__.py @@ -1,12 +1,19 @@ -import re import copy +import os +import re +import struct from datetime import datetime, date, time from dateutil import parser from .exceptions import ParserError +with_extensions = os.getenv("PENDULUM_EXTENSIONS", "1") == "1" + try: + if not with_extensions or struct.calcsize("P") == 4: + raise ImportError() + from ._iso8601 import parse_iso8601 except ImportError: from .iso8601 import parse_iso8601 From abcdc92404b350a27aa3edd1e3612470192cb116 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Eustace?= Date: Tue, 30 Oct 2018 12:18:36 -0500 Subject: [PATCH 021/256] Bump version to 2.0.4 --- CHANGELOG.md | 5 +++-- pendulum/__version__.py | 2 +- pyproject.toml | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 518f7578..427daec2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Change Log -## [Unreleased] +## [2.0.4] - 2018-10-30 ### Fixed @@ -80,7 +80,8 @@ -[Unreleased]: https://github.com/sdispater/pendulum/compare/2.0.3...master +[Unreleased]: https://github.com/sdispater/pendulum/compare/2.0.4...master +[2.0.4]: https://github.com/sdispater/pendulum/releases/tag/2.0.4 [2.0.3]: https://github.com/sdispater/pendulum/releases/tag/2.0.3 [2.0.2]: https://github.com/sdispater/pendulum/releases/tag/2.0.2 [2.0.1]: https://github.com/sdispater/pendulum/releases/tag/2.0.1 diff --git a/pendulum/__version__.py b/pendulum/__version__.py index 5fa9130a..f6bb6f4d 100644 --- a/pendulum/__version__.py +++ b/pendulum/__version__.py @@ -1 +1 @@ -__version__ = "2.0.3" +__version__ = "2.0.4" diff --git a/pyproject.toml b/pyproject.toml index 14ae6f5b..0449aa99 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "pendulum" -version = "2.0.3" +version = "2.0.4" description = "Python datetimes made easy" authors = ["Sébastien Eustace "] license = "MIT" From 19d43e80a2b49deec37aa6a5f202ac44282c8461 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Eustace?= Date: Tue, 30 Oct 2018 12:47:55 -0500 Subject: [PATCH 022/256] Fix build files --- Makefile | 2 +- build-wheels.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 0540be28..a1677b48 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ # http://www.opensource.org/licenses/MIT-license # Copyright (c) 2015 Sébastien Eustace -PENDULUM_RELEASE := $$(sed -n -E "s/VERSION = '(.+)'/\1/p" pendulum/version.py) +PENDULUM_RELEASE := $$(sed -n -E "s/VERSION = \"(.+)\"/\1/p" pendulum/version.py) # lists all available targets list: diff --git a/build-wheels.sh b/build-wheels.sh index 83a4d1e7..1ae9eed1 100755 --- a/build-wheels.sh +++ b/build-wheels.sh @@ -8,7 +8,7 @@ echo "Create Poetry's virtualenv" /opt/python/${POETRY_PYTHON}/bin/virtualenv --python /opt/python/${POETRY_PYTHON}/bin/python ${POETRY_VENV} ${POETRY_VENV}/bin/pip install poetry --pre -RELEASE=$(sed -n "s/__version__ = '\(.*\)'/\1/p" /io/pendulum/__version__.py) +RELEASE=$(sed -n "s/__version__ = \"\(.*\)\"/\1/p" /io/pendulum/__version__.py) echo "Compile wheels" for PYTHON in ${PYTHON_VERSIONS}; do From f11571c903adcfbf2344d447221407a23da4fea2 Mon Sep 17 00:00:00 2001 From: Bryan Forbes Date: Fri, 30 Nov 2018 11:44:19 -0600 Subject: [PATCH 023/256] Handle transition.previous being None --- pendulum/tz/timezone.py | 10 +++++----- pendulum/tz/zoneinfo/transition.py | 9 ++++++--- tests/tz/test_timezone.py | 6 ++++++ 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/pendulum/tz/timezone.py b/pendulum/tz/timezone.py index a79242ed..e10ec00a 100644 --- a/pendulum/tz/timezone.py +++ b/pendulum/tz/timezone.py @@ -105,7 +105,7 @@ def _normalize( # We set the fold attribute for later if dst_rule == POST_TRANSITION: fold = 1 - else: + elif transition.previous is not None: transition = transition.previous if transition.is_ambiguous(sec): @@ -149,7 +149,7 @@ def _convert(self, dt): # type: (datetime) -> datetime transition = dt.tzinfo._lookup_transition(stamp) offset = transition.ttype.offset - if stamp < transition.local: + if stamp < transition.local and transition.previous is not None: if ( transition.previous.is_ambiguous(stamp) and getattr(dt, "fold", 1) == 0 @@ -161,7 +161,7 @@ def _convert(self, dt): # type: (datetime) -> datetime stamp -= offset transition = self._lookup_transition(stamp, is_utc=True) - if stamp < transition.at: + if stamp < transition.at and transition.previous is not None: transition = transition.previous offset = transition.ttype.offset @@ -254,7 +254,7 @@ def _get_transition(self, dt): # type: (datetime) -> Transition transition = self._lookup_transition(stamp) - if stamp < transition.local: + if stamp < transition.local and transition.previous is not None: fold = getattr(dt, "fold", 1) if transition.is_ambiguous(stamp): if fold == 0: @@ -270,7 +270,7 @@ def fromutc(self, dt): # type: (datetime) -> datetime stamp = timestamp(dt) transition = self._lookup_transition(stamp, is_utc=True) - if stamp < transition.at: + if stamp < transition.at and transition.previous is not None: transition = transition.previous stamp += transition.ttype.offset diff --git a/pendulum/tz/zoneinfo/transition.py b/pendulum/tz/zoneinfo/transition.py index 1ba93a46..278f7cd6 100644 --- a/pendulum/tz/zoneinfo/transition.py +++ b/pendulum/tz/zoneinfo/transition.py @@ -1,5 +1,5 @@ from datetime import timedelta -from typing import Union +from typing import Union, Optional from .transition_type import TransitionType @@ -51,7 +51,7 @@ def ttype(self): # type: () -> TransitionType return self._ttype @property - def previous(self): # type: () -> Transition + def previous(self): # type: () -> Optional[Transition] return self._previous @property @@ -67,7 +67,10 @@ def is_missing(self, stamp): # type: (int) -> bool def utcoffset(self): # type: () -> timedelta return self._utcoffset - def __contains__(self, stamp): # type: () -> bool + def __contains__(self, stamp): # type: (int) -> bool + if self.previous is None: + return stamp < self.local + return self.previous.local <= stamp < self.local def __repr__(self): # type: () -> str diff --git a/tests/tz/test_timezone.py b/tests/tz/test_timezone.py index 7d407a8d..31d8c3dc 100644 --- a/tests/tz/test_timezone.py +++ b/tests/tz/test_timezone.py @@ -303,6 +303,12 @@ def test_utcoffset(): assert utcoffset == timedelta(0, -18000) +def test_utcoffset_pre_transition(): + tz = pendulum.timezone("America/Chicago") + utcoffset = tz.utcoffset(datetime(1883, 11, 18)) + assert utcoffset == timedelta(days=-1, seconds=64800) + + def test_dst(): tz = pendulum.timezone("Europe/Amsterdam") dst = tz.dst(datetime(1940, 7, 1)) From 23f290f8063a52c77a2ae7de7a790f91a84dc817 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Eustace?= Date: Thu, 3 Jan 2019 16:16:33 +0100 Subject: [PATCH 024/256] Fix ISO week dates not being parsed properly in from_format() --- CHANGELOG.md | 7 +++++++ pendulum/formatting/formatter.py | 2 +- tests/datetime/test_from_format.py | 1 + 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 427daec2..8fb79b45 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Change Log +## [Unreleased] + +### Fixed + +- Fixed ISO week dates not being parsed properly in `fom_format()`. + + ## [2.0.4] - 2018-10-30 ### Fixed diff --git a/pendulum/formatting/formatter.py b/pendulum/formatting/formatter.py index 712e65df..c6ed389a 100644 --- a/pendulum/formatting/formatter.py +++ b/pendulum/formatting/formatter.py @@ -469,7 +469,7 @@ def _check_parsed(self, parsed, now): # type: (dict, pendulum.DateTime) -> dict if parsed["day_of_year"] is not None: dt = pendulum.parse( - "{}-{}".format(validated["year"], parsed["day_of_year"]) + "{}-{:>03d}".format(validated["year"], parsed["day_of_year"]) ) validated["month"] = dt.month diff --git a/tests/datetime/test_from_format.py b/tests/datetime/test_from_format.py index 97fa8411..c5aeaa27 100644 --- a/tests/datetime/test_from_format.py +++ b/tests/datetime/test_from_format.py @@ -132,6 +132,7 @@ def test_from_format_with_invalid_padded_day(): "1975-12-25T14:15:16+03:00", None, ), + ("190022215", "YYDDDDHHmm", "2019-01-02T22:15:00+00:00", None), ], ) def test_from_format(text, fmt, expected, now): From b545b77c412c7e8eb0db54a2b8cb8de321fd6e0c Mon Sep 17 00:00:00 2001 From: Jace Browning Date: Sat, 2 Mar 2019 21:28:10 -0500 Subject: [PATCH 025/256] Fix case where transition can be None --- pendulum/tz/timezone.py | 39 ++++++++++++++++++++------------------- tests/date/test_sub.py | 7 ++++++- 2 files changed, 26 insertions(+), 20 deletions(-) diff --git a/pendulum/tz/timezone.py b/pendulum/tz/timezone.py index a79242ed..60e38554 100644 --- a/pendulum/tz/timezone.py +++ b/pendulum/tz/timezone.py @@ -108,25 +108,26 @@ def _normalize( else: transition = transition.previous - if transition.is_ambiguous(sec): - # Ambiguous time - if dst_rule == TRANSITION_ERROR: - raise AmbiguousTime(dt) - - # We set the fold attribute for later - if dst_rule == POST_TRANSITION: - fold = 1 - elif transition.is_missing(sec): - # Skipped time - if dst_rule == TRANSITION_ERROR: - raise NonExistingTime(dt) - - # We adjust accordingly - if dst_rule == POST_TRANSITION: - sec += transition.fix - fold = 1 - else: - sec -= transition.fix + if transition: + if transition.is_ambiguous(sec): + # Ambiguous time + if dst_rule == TRANSITION_ERROR: + raise AmbiguousTime(dt) + + # We set the fold attribute for later + if dst_rule == POST_TRANSITION: + fold = 1 + elif transition.is_missing(sec): + # Skipped time + if dst_rule == TRANSITION_ERROR: + raise NonExistingTime(dt) + + # We adjust accordingly + if dst_rule == POST_TRANSITION: + sec += transition.fix + fold = 1 + else: + sec -= transition.fix kwargs = {"tzinfo": self} if _HAS_FOLD or isinstance(dt, pendulum.DateTime): diff --git a/tests/date/test_sub.py b/tests/date/test_sub.py index b7f781af..1edbd463 100644 --- a/tests/date/test_sub.py +++ b/tests/date/test_sub.py @@ -2,7 +2,7 @@ import pendulum -from datetime import timedelta +from datetime import datetime, timedelta from ..conftest import assert_date @@ -43,6 +43,11 @@ def test_subtract_days_negative(): assert pendulum.Date(1975, 5, 30).subtract(days=-1).day == 31 +def test_subtract_days_max(): + delta = pendulum.now() - pendulum.instance(datetime.min) + assert pendulum.now().subtract(days=delta.days - 1).year == 1 + + def test_subtract_weeks_positive(): assert pendulum.Date(1975, 5, 28).subtract(weeks=1).day == 21 From 6df9baf85806501e02d07b5e9b151271a8e7e43e Mon Sep 17 00:00:00 2001 From: Kyle Altendorf Date: Mon, 11 Mar 2019 17:33:16 -0400 Subject: [PATCH 026/256] Improve test_nth_of_month_outside_scope() test cases --- tests/date/test_day_of_week_modifiers.py | 2 +- tests/datetime/test_day_of_week_modifiers.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/date/test_day_of_week_modifiers.py b/tests/date/test_day_of_week_modifiers.py index 0b12a98f..e65a13c0 100644 --- a/tests/date/test_day_of_week_modifiers.py +++ b/tests/date/test_day_of_week_modifiers.py @@ -111,7 +111,7 @@ def test_last_friday_of_month(): def test_nth_of_month_outside_scope(): - d = pendulum.date(1975, 12, 5) + d = pendulum.date(1975, 6, 5) with pytest.raises(PendulumException): d.nth_of("month", 6, pendulum.MONDAY) diff --git a/tests/datetime/test_day_of_week_modifiers.py b/tests/datetime/test_day_of_week_modifiers.py index a1091022..7b476418 100644 --- a/tests/datetime/test_day_of_week_modifiers.py +++ b/tests/datetime/test_day_of_week_modifiers.py @@ -127,7 +127,7 @@ def test_last_friday_of_month(): def test_nth_of_month_outside_scope(): - d = pendulum.datetime(1975, 12, 5) + d = pendulum.datetime(1975, 6, 5) with pytest.raises(PendulumException): d.nth_of("month", 6, pendulum.MONDAY) From 5f895d95fcbec85c2ad9ae12602c4713e450b6d4 Mon Sep 17 00:00:00 2001 From: Kyle Altendorf Date: Mon, 11 Mar 2019 17:46:44 -0400 Subject: [PATCH 027/256] Correct minute comparison to month for DateTime.nth_of_month() --- pendulum/datetime.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pendulum/datetime.py b/pendulum/datetime.py index abca24a8..6c423037 100644 --- a/pendulum/datetime.py +++ b/pendulum/datetime.py @@ -1256,11 +1256,11 @@ def _nth_of_month(self, nth, day_of_week): return self.first_of("month", day_of_week) dt = self.first_of("month") - check = dt.format("%Y-%m") + check = dt.format("%Y-%M") for i in range(nth - (1 if dt.day_of_week == day_of_week else 0)): dt = dt.next(day_of_week) - if dt.format("%Y-%m") == check: + if dt.format("%Y-%M") == check: return self.set(day=dt.day).start_of("day") return False From 94d28b0d3cb524ae02361bd1ed7ea03e2e655e4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Eustace?= Date: Sun, 17 Mar 2019 21:57:20 +0100 Subject: [PATCH 028/256] Fix loading of some timezones with empty posix spec --- pendulum/tz/zoneinfo/reader.py | 7 +++++-- tests/tz/test_timezones.py | 9 ++++----- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/pendulum/tz/zoneinfo/reader.py b/pendulum/tz/zoneinfo/reader.py index 77fd0454..f494197d 100644 --- a/pendulum/tz/zoneinfo/reader.py +++ b/pendulum/tz/zoneinfo/reader.py @@ -3,7 +3,7 @@ from collections import namedtuple from struct import unpack -from typing import List, Dict +from typing import Dict, List, Optional from pytzdata.exceptions import TimezoneNotFound @@ -200,7 +200,7 @@ def _parse_abbrs( return abbrs - def _parse_posix_tz(self, fd): # type: (...) -> PosixTimezone + def _parse_posix_tz(self, fd): # type: (...) -> Optional[PosixTimezone] s = fd.read().decode("utf-8") if not s.startswith("\n") or not s.endswith("\n"): @@ -208,4 +208,7 @@ def _parse_posix_tz(self, fd): # type: (...) -> PosixTimezone s = s.strip() + if not s: + return + return posix_spec(s) diff --git a/tests/tz/test_timezones.py b/tests/tz/test_timezones.py index 3bb1e615..56c8db15 100644 --- a/tests/tz/test_timezones.py +++ b/tests/tz/test_timezones.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +import pytest import pendulum @@ -9,8 +10,6 @@ def test_timezones(): assert "America/Argentina/Buenos_Aires" in zones -def test_timezones_are_loadable(): - zones = pendulum.timezones - - for zone in zones: - pendulum.timezone(zone) +@pytest.mark.parametrize("zone", [zone for zone in pendulum.timezones]) +def test_timezones_are_loadable(zone): + pendulum.timezone(zone) From 0a96016cd64426390a556544776417a8de3a792d Mon Sep 17 00:00:00 2001 From: Kyle Altendorf Date: Tue, 21 May 2019 15:40:51 -0400 Subject: [PATCH 029/256] Address four deprecation warnings (#375) tests/formatting/test_formatter.py:241 /home/altendky/pendulum/tests/formatting/test_formatter.py:241: DeprecationWarning: invalid escape sequence \[ assert f.format(d, "[YYYY] YYYY \[YYYY\]") == "YYYY 2016 [2016]" tests/formatting/test_formatter.py:242 /home/altendky/pendulum/tests/formatting/test_formatter.py:242: DeprecationWarning: invalid escape sequence \D assert f.format(d, "\D D \\\D") == "D 28 \\28" tests/datetime/test_from_format.py::test_from_format_with_escaped_elements /home/altendky/pendulum/pendulum/formatting/formatter.py:406: DeprecationWarning: Flags not at the start of the expression '(?P\\d{1,4}|\\d{' (truncated) if not re.match(pattern, time): tests/datetime/test_from_format.py::test_from_format[Thursday 25th December 1975 02:15:16 PM -05:00-dddd Do MMMM YYYY hh:mm:ss A Z-1975-12-25T14:15:16-05:00-None] /home/altendky/pendulum/pendulum/formatting/formatter.py:406: DeprecationWarning: Flags not at the start of the expression '(?PSunday|Mond' (truncated) if not re.match(pattern, time): --- pendulum/formatting/formatter.py | 4 ++-- tests/formatting/test_formatter.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pendulum/formatting/formatter.py b/pendulum/formatting/formatter.py index c6ed389a..76a4db4f 100644 --- a/pendulum/formatting/formatter.py +++ b/pendulum/formatting/formatter.py @@ -25,8 +25,8 @@ _MATCH_5_TO_6 = "\d{5}\d?" _MATCH_UNSIGNED = "\d+" _MATCH_SIGNED = "[+-]?\d+" -_MATCH_OFFSET = "(?i)Z|[+-]\d\d:?\d\d" -_MATCH_SHORT_OFFSET = "(?i)Z|[+-]\d\d(?::?\d\d)?" +_MATCH_OFFSET = "[Zz]|[+-]\d\d:?\d\d" +_MATCH_SHORT_OFFSET = "[Zz]|[+-]\d\d(?::?\d\d)?" _MATCH_TIMESTAMP = "[+-]?\d+(\.\d{1,6})?" _MATCH_WORD = ( "(?i)[0-9]*" diff --git a/tests/formatting/test_formatter.py b/tests/formatting/test_formatter.py index 40f2ee8e..80bb4517 100644 --- a/tests/formatting/test_formatter.py +++ b/tests/formatting/test_formatter.py @@ -238,8 +238,8 @@ def test_date_formats(): def test_escape(): f = Formatter() d = pendulum.datetime(2016, 8, 28) - assert f.format(d, "[YYYY] YYYY \[YYYY\]") == "YYYY 2016 [2016]" - assert f.format(d, "\D D \\\D") == "D 28 \\28" + assert f.format(d, r"[YYYY] YYYY \[YYYY\]") == "YYYY 2016 [2016]" + assert f.format(d, r"\D D \\D") == "D 28 \\28" def test_date_formats_missing(): From 0962a6e6e631416001c6b9abd6da2b9d0cac947f Mon Sep 17 00:00:00 2001 From: Kovalchuk Evgeny Date: Tue, 21 May 2019 22:42:04 +0300 Subject: [PATCH 030/256] Add RU locale (#228) * Add RU locale * Show "few seconds" as numbers, as in 1.x for RU locale * Add RU locale tests * Fix styling in locale.py --- pendulum/locales/ru/__init__.py | 0 pendulum/locales/ru/custom.py | 23 +++ pendulum/locales/ru/locale.py | 260 ++++++++++++++++++++++++++++++++ tests/localization/test_ru.py | 88 +++++++++++ 4 files changed, 371 insertions(+) create mode 100644 pendulum/locales/ru/__init__.py create mode 100644 pendulum/locales/ru/custom.py create mode 100644 pendulum/locales/ru/locale.py create mode 100644 tests/localization/test_ru.py diff --git a/pendulum/locales/ru/__init__.py b/pendulum/locales/ru/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pendulum/locales/ru/custom.py b/pendulum/locales/ru/custom.py new file mode 100644 index 00000000..00d0f012 --- /dev/null +++ b/pendulum/locales/ru/custom.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals +""" +ru custom locale file. +""" + +translations = { + # Relative time + 'ago': '{} назад', + 'from_now': 'через {}', + 'after': '{0} после', + 'before': '{0} до', + + # Date formats + 'date_formats': { + 'LTS': 'HH:mm:ss', + 'LT': 'HH:mm', + 'L': 'DD.MM.YYYY', + 'LL': 'D MMMM YYYY г.', + 'LLL': 'D MMMM YYYY г., HH:mm', + 'LLLL': 'dddd, D MMMM YYYY г., HH:mm' + }, +} diff --git a/pendulum/locales/ru/locale.py b/pendulum/locales/ru/locale.py new file mode 100644 index 00000000..0fb85ff7 --- /dev/null +++ b/pendulum/locales/ru/locale.py @@ -0,0 +1,260 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals +""" +ru locale file. + +It has been generated automatically and must not be modified directly. +""" + +from .custom import translations as custom_translations + + +locale = { + 'plural': lambda n: 'few' if (((0 == 0 and ((0 == 0))) and ((n % 10) == (n % 10) and (((n % 10) >= 2 and (n % 10) <= 4)))) and (not ((n % 100) == (n % 100) and (((n % 100) >= 12 and (n % 100) <= 14))))) else 'many' if ((((0 == 0 and ((0 == 0))) and ((n % 10) == (n % 10) and (((n % 10) == 0)))) or ((0 == 0 and ((0 == 0))) and ((n % 10) == (n % 10) and (((n % 10) >= 5 and (n % 10) <= 9))))) or ((0 == 0 and ((0 == 0))) and ((n % 100) == (n % 100) and (((n % 100) >= 11 and (n % 100) <= 14))))) else 'one' if (((0 == 0 and ((0 == 0))) and ((n % 10) == (n % 10) and (((n % 10) == 1)))) and (not ((n % 100) == (n % 100) and (((n % 100) == 11))))) else 'other', + 'ordinal': lambda n: 'other', + 'translations': { + 'days': { + 'abbreviated': { + 0: 'вс', + 1: 'пн', + 2: 'вт', + 3: 'ср', + 4: 'чт', + 5: 'пт', + 6: 'сб', + }, + 'narrow': { + 0: 'вс', + 1: 'пн', + 2: 'вт', + 3: 'ср', + 4: 'чт', + 5: 'пт', + 6: 'сб', + }, + 'short': { + 0: 'вс', + 1: 'пн', + 2: 'вт', + 3: 'ср', + 4: 'чт', + 5: 'пт', + 6: 'сб', + }, + 'wide': { + 0: 'воскресенье', + 1: 'понедельник', + 2: 'вторник', + 3: 'среда', + 4: 'четверг', + 5: 'пятница', + 6: 'суббота', + }, + }, + 'months': { + 'abbreviated': { + 1: 'янв.', + 2: 'февр.', + 3: 'мар.', + 4: 'апр.', + 5: 'мая', + 6: 'июн.', + 7: 'июл.', + 8: 'авг.', + 9: 'сент.', + 10: 'окт.', + 11: 'нояб.', + 12: 'дек.', + }, + 'narrow': { + 1: 'Я', + 2: 'Ф', + 3: 'М', + 4: 'А', + 5: 'М', + 6: 'И', + 7: 'И', + 8: 'А', + 9: 'С', + 10: 'О', + 11: 'Н', + 12: 'Д', + }, + 'wide': { + 1: 'января', + 2: 'февраля', + 3: 'марта', + 4: 'апреля', + 5: 'мая', + 6: 'июня', + 7: 'июля', + 8: 'августа', + 9: 'сентября', + 10: 'октября', + 11: 'ноября', + 12: 'декабря', + }, + }, + 'units': { + 'year': { + 'one': '{0} год', + 'few': '{0} года', + 'many': '{0} лет', + 'other': '{0} года', + }, + 'month': { + 'one': '{0} месяц', + 'few': '{0} месяца', + 'many': '{0} месяцев', + 'other': '{0} месяца', + }, + 'week': { + 'one': '{0} неделя', + 'few': '{0} недели', + 'many': '{0} недель', + 'other': '{0} недели', + }, + 'day': { + 'one': '{0} день', + 'few': '{0} дня', + 'many': '{0} дней', + 'other': '{0} дня', + }, + 'hour': { + 'one': '{0} час', + 'few': '{0} часа', + 'many': '{0} часов', + 'other': '{0} часа', + }, + 'minute': { + 'one': '{0} минута', + 'few': '{0} минуты', + 'many': '{0} минут', + 'other': '{0} минуты', + }, + 'second': { + 'one': '{0} секунда', + 'few': '{0} секунды', + 'many': '{0} секунд', + 'other': '{0} секунды', + }, + 'microsecond': { + 'one': '{0} микросекунда', + 'few': '{0} микросекунды', + 'many': '{0} микросекунд', + 'other': '{0} микросекунды', + }, + }, + 'relative': { + 'year': { + 'future': { + 'other': 'через {0} года', + 'one': 'через {0} год', + 'few': 'через {0} года', + 'many': 'через {0} лет', + }, + 'past': { + 'other': '{0} года назад', + 'one': '{0} год назад', + 'few': '{0} года назад', + 'many': '{0} лет назад', + }, + }, + 'month': { + 'future': { + 'other': 'через {0} месяца', + 'one': 'через {0} месяц', + 'few': 'через {0} месяца', + 'many': 'через {0} месяцев', + }, + 'past': { + 'other': '{0} месяца назад', + 'one': '{0} месяц назад', + 'few': '{0} месяца назад', + 'many': '{0} месяцев назад', + }, + }, + 'week': { + 'future': { + 'other': 'через {0} недели', + 'one': 'через {0} неделю', + 'few': 'через {0} недели', + 'many': 'через {0} недель', + }, + 'past': { + 'other': '{0} недели назад', + 'one': '{0} неделю назад', + 'few': '{0} недели назад', + 'many': '{0} недель назад', + }, + }, + 'day': { + 'future': { + 'other': 'через {0} дня', + 'one': 'через {0} день', + 'few': 'через {0} дня', + 'many': 'через {0} дней', + }, + 'past': { + 'other': '{0} дня назад', + 'one': '{0} день назад', + 'few': '{0} дня назад', + 'many': '{0} дней назад', + }, + }, + 'hour': { + 'future': { + 'other': 'через {0} часа', + 'one': 'через {0} час', + 'few': 'через {0} часа', + 'many': 'через {0} часов', + }, + 'past': { + 'other': '{0} часа назад', + 'one': '{0} час назад', + 'few': '{0} часа назад', + 'many': '{0} часов назад', + }, + }, + 'minute': { + 'future': { + 'other': 'через {0} минуты', + 'one': 'через {0} минуту', + 'few': 'через {0} минуты', + 'many': 'через {0} минут', + }, + 'past': { + 'other': '{0} минуты назад', + 'one': '{0} минуту назад', + 'few': '{0} минуты назад', + 'many': '{0} минут назад', + }, + }, + 'second': { + 'future': { + 'other': 'через {0} секунды', + 'one': 'через {0} секунду', + 'few': 'через {0} секунды', + 'many': 'через {0} секунд', + }, + 'past': { + 'other': '{0} секунды назад', + 'one': '{0} секунду назад', + 'few': '{0} секунды назад', + 'many': '{0} секунд назад', + }, + }, + }, + 'day_periods': { + 'midnight': 'полночь', + 'am': 'AM', + 'noon': 'полдень', + 'pm': 'PM', + 'morning1': 'утра', + 'afternoon1': 'дня', + 'evening1': 'вечера', + 'night1': 'ночи', + }, + }, + 'custom': custom_translations +} diff --git a/tests/localization/test_ru.py b/tests/localization/test_ru.py new file mode 100644 index 00000000..e5003f14 --- /dev/null +++ b/tests/localization/test_ru.py @@ -0,0 +1,88 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +import pendulum + + +locale = 'ru' + + +def test_diff_for_humans(): + with pendulum.test(pendulum.datetime(2016, 8, 29)): + diff_for_humans() + + +def diff_for_humans(): + d = pendulum.now().subtract(seconds=1) + assert d.diff_for_humans(locale=locale) == '1 секунду назад' + + d = pendulum.now().subtract(seconds=2) + assert d.diff_for_humans(locale=locale) == '2 секунды назад' + + d = pendulum.now().subtract(seconds=5) + assert d.diff_for_humans(locale=locale) == '5 секунд назад' + + d = pendulum.now().subtract(seconds=21) + assert d.diff_for_humans(locale=locale) == '21 секунду назад' + + d = pendulum.now().subtract(minutes=1) + assert d.diff_for_humans(locale=locale) == '1 минуту назад' + + d = pendulum.now().subtract(minutes=2) + assert d.diff_for_humans(locale=locale) == '2 минуты назад' + + d = pendulum.now().subtract(minutes=5) + assert d.diff_for_humans(locale=locale) == '5 минут назад' + + d = pendulum.now().subtract(hours=1) + assert d.diff_for_humans(locale=locale) == '1 час назад' + + d = pendulum.now().subtract(hours=2) + assert d.diff_for_humans(locale=locale) == '2 часа назад' + + d = pendulum.now().subtract(hours=5) + assert d.diff_for_humans(locale=locale) == '5 часов назад' + + d = pendulum.now().subtract(days=1) + assert d.diff_for_humans(locale=locale) == '1 день назад' + + d = pendulum.now().subtract(days=2) + assert d.diff_for_humans(locale=locale) == '2 дня назад' + + d = pendulum.now().subtract(days=5) + assert d.diff_for_humans(locale=locale) == '5 дней назад' + + d = pendulum.now().subtract(weeks=1) + assert d.diff_for_humans(locale=locale) == '1 неделю назад' + + d = pendulum.now().subtract(weeks=2) + assert d.diff_for_humans(locale=locale) == '2 недели назад' + + d = pendulum.now().subtract(months=1) + assert d.diff_for_humans(locale=locale) == '1 месяц назад' + + d = pendulum.now().subtract(months=2) + assert d.diff_for_humans(locale=locale) == '2 месяца назад' + + d = pendulum.now().subtract(months=5) + assert d.diff_for_humans(locale=locale) == '5 месяцев назад' + + d = pendulum.now().subtract(years=1) + assert d.diff_for_humans(locale=locale) == '1 год назад' + + d = pendulum.now().subtract(years=2) + assert d.diff_for_humans(locale=locale) == '2 года назад' + + d = pendulum.now().subtract(years=5) + assert d.diff_for_humans(locale=locale) == '5 лет назад' + + d = pendulum.now().add(seconds=1) + assert d.diff_for_humans(locale=locale) == 'через 1 секунду' + + d = pendulum.now().add(seconds=1) + d2 = pendulum.now() + assert d.diff_for_humans(d2, locale=locale) == '1 секунда после' + assert d2.diff_for_humans(d, locale=locale) == '1 секунда до' + + assert d.diff_for_humans(d2, True, locale=locale) == '1 секунда' + assert d2.diff_for_humans(d.add(seconds=1), True, locale=locale) == '2 секунды' From ef10a8eb5d201c063059d8e46c0c916ce64fb2f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Eustace?= Date: Wed, 3 Jul 2019 21:15:48 +0200 Subject: [PATCH 031/256] Update tox config --- tox.ini | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tox.ini b/tox.ini index 072640cc..12d63c9b 100644 --- a/tox.ini +++ b/tox.ini @@ -1,10 +1,9 @@ [tox] -skipsdist = True -envlist = py27, py35, py36, pypy, pypy3 +isolated_build = true +envlist = py27, py35, py36, py37, py38, pypy, pypy3 [testenv] whitelist_externals = poetry -skip_install = true commands = poetry install -v poetry run pytest tests/ From 46855889dea20d4020d468287122336b3c2a9013 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Eustace?= Date: Wed, 3 Jul 2019 21:18:03 +0200 Subject: [PATCH 032/256] Update change log --- CHANGELOG.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8fb79b45..76cf8206 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,13 @@ ### Fixed -- Fixed ISO week dates not being parsed properly in `fom_format()`. +- Fixed ISO week dates not being parsed properly in `from_format()`. +- Fixed loading of some timezones with empty posix spec. +- Fixed deprecation warnings. + +### Locales + +- Added RU locale. ## [2.0.4] - 2018-10-30 From f629fa67950d071e861124a61c4b4cba1f513eb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Eustace?= Date: Wed, 3 Jul 2019 21:23:13 +0200 Subject: [PATCH 033/256] Fix black formatting --- CHANGELOG.md | 5 +- pendulum/__version__.py | 2 +- pendulum/locales/ru/custom.py | 24 +- pendulum/locales/ru/locale.py | 426 +++++++++++++++++----------------- pyproject.toml | 4 +- tests/localization/test_ru.py | 54 ++--- 6 files changed, 264 insertions(+), 251 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 76cf8206..4553170e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Change Log -## [Unreleased] +## [2.0.5] - 2019-07-03 ### Fixed @@ -93,7 +93,8 @@ -[Unreleased]: https://github.com/sdispater/pendulum/compare/2.0.4...master +[Unreleased]: https://github.com/sdispater/pendulum/compare/2.0.5...master +[2.0.5]: https://github.com/sdispater/pendulum/releases/tag/2.0.5 [2.0.4]: https://github.com/sdispater/pendulum/releases/tag/2.0.4 [2.0.3]: https://github.com/sdispater/pendulum/releases/tag/2.0.3 [2.0.2]: https://github.com/sdispater/pendulum/releases/tag/2.0.2 diff --git a/pendulum/__version__.py b/pendulum/__version__.py index f6bb6f4d..34c5111c 100644 --- a/pendulum/__version__.py +++ b/pendulum/__version__.py @@ -1 +1 @@ -__version__ = "2.0.4" +__version__ = "2.0.5" diff --git a/pendulum/locales/ru/custom.py b/pendulum/locales/ru/custom.py index 00d0f012..a9fe40e3 100644 --- a/pendulum/locales/ru/custom.py +++ b/pendulum/locales/ru/custom.py @@ -1,23 +1,23 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals + """ ru custom locale file. """ translations = { # Relative time - 'ago': '{} назад', - 'from_now': 'через {}', - 'after': '{0} после', - 'before': '{0} до', - + "ago": "{} назад", + "from_now": "через {}", + "after": "{0} после", + "before": "{0} до", # Date formats - 'date_formats': { - 'LTS': 'HH:mm:ss', - 'LT': 'HH:mm', - 'L': 'DD.MM.YYYY', - 'LL': 'D MMMM YYYY г.', - 'LLL': 'D MMMM YYYY г., HH:mm', - 'LLLL': 'dddd, D MMMM YYYY г., HH:mm' + "date_formats": { + "LTS": "HH:mm:ss", + "LT": "HH:mm", + "L": "DD.MM.YYYY", + "LL": "D MMMM YYYY г.", + "LLL": "D MMMM YYYY г., HH:mm", + "LLLL": "dddd, D MMMM YYYY г., HH:mm", }, } diff --git a/pendulum/locales/ru/locale.py b/pendulum/locales/ru/locale.py index 0fb85ff7..4ad89478 100644 --- a/pendulum/locales/ru/locale.py +++ b/pendulum/locales/ru/locale.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals + """ ru locale file. @@ -10,251 +11,262 @@ locale = { - 'plural': lambda n: 'few' if (((0 == 0 and ((0 == 0))) and ((n % 10) == (n % 10) and (((n % 10) >= 2 and (n % 10) <= 4)))) and (not ((n % 100) == (n % 100) and (((n % 100) >= 12 and (n % 100) <= 14))))) else 'many' if ((((0 == 0 and ((0 == 0))) and ((n % 10) == (n % 10) and (((n % 10) == 0)))) or ((0 == 0 and ((0 == 0))) and ((n % 10) == (n % 10) and (((n % 10) >= 5 and (n % 10) <= 9))))) or ((0 == 0 and ((0 == 0))) and ((n % 100) == (n % 100) and (((n % 100) >= 11 and (n % 100) <= 14))))) else 'one' if (((0 == 0 and ((0 == 0))) and ((n % 10) == (n % 10) and (((n % 10) == 1)))) and (not ((n % 100) == (n % 100) and (((n % 100) == 11))))) else 'other', - 'ordinal': lambda n: 'other', - 'translations': { - 'days': { - 'abbreviated': { - 0: 'вс', - 1: 'пн', - 2: 'вт', - 3: 'ср', - 4: 'чт', - 5: 'пт', - 6: 'сб', - }, - 'narrow': { - 0: 'вс', - 1: 'пн', - 2: 'вт', - 3: 'ср', - 4: 'чт', - 5: 'пт', - 6: 'сб', - }, - 'short': { - 0: 'вс', - 1: 'пн', - 2: 'вт', - 3: 'ср', - 4: 'чт', - 5: 'пт', - 6: 'сб', + "plural": lambda n: "few" + if ( + ( + (0 == 0 and ((0 == 0))) + and ((n % 10) == (n % 10) and (((n % 10) >= 2 and (n % 10) <= 4))) + ) + and (not ((n % 100) == (n % 100) and (((n % 100) >= 12 and (n % 100) <= 14)))) + ) + else "many" + if ( + ( + ((0 == 0 and ((0 == 0))) and ((n % 10) == (n % 10) and (((n % 10) == 0)))) + or ( + (0 == 0 and ((0 == 0))) + and ((n % 10) == (n % 10) and (((n % 10) >= 5 and (n % 10) <= 9))) + ) + ) + or ( + (0 == 0 and ((0 == 0))) + and ((n % 100) == (n % 100) and (((n % 100) >= 11 and (n % 100) <= 14))) + ) + ) + else "one" + if ( + ((0 == 0 and ((0 == 0))) and ((n % 10) == (n % 10) and (((n % 10) == 1)))) + and (not ((n % 100) == (n % 100) and (((n % 100) == 11)))) + ) + else "other", + "ordinal": lambda n: "other", + "translations": { + "days": { + "abbreviated": { + 0: "вс", + 1: "пн", + 2: "вт", + 3: "ср", + 4: "чт", + 5: "пт", + 6: "сб", }, - 'wide': { - 0: 'воскресенье', - 1: 'понедельник', - 2: 'вторник', - 3: 'среда', - 4: 'четверг', - 5: 'пятница', - 6: 'суббота', + "narrow": {0: "вс", 1: "пн", 2: "вт", 3: "ср", 4: "чт", 5: "пт", 6: "сб"}, + "short": {0: "вс", 1: "пн", 2: "вт", 3: "ср", 4: "чт", 5: "пт", 6: "сб"}, + "wide": { + 0: "воскресенье", + 1: "понедельник", + 2: "вторник", + 3: "среда", + 4: "четверг", + 5: "пятница", + 6: "суббота", }, }, - 'months': { - 'abbreviated': { - 1: 'янв.', - 2: 'февр.', - 3: 'мар.', - 4: 'апр.', - 5: 'мая', - 6: 'июн.', - 7: 'июл.', - 8: 'авг.', - 9: 'сент.', - 10: 'окт.', - 11: 'нояб.', - 12: 'дек.', + "months": { + "abbreviated": { + 1: "янв.", + 2: "февр.", + 3: "мар.", + 4: "апр.", + 5: "мая", + 6: "июн.", + 7: "июл.", + 8: "авг.", + 9: "сент.", + 10: "окт.", + 11: "нояб.", + 12: "дек.", }, - 'narrow': { - 1: 'Я', - 2: 'Ф', - 3: 'М', - 4: 'А', - 5: 'М', - 6: 'И', - 7: 'И', - 8: 'А', - 9: 'С', - 10: 'О', - 11: 'Н', - 12: 'Д', + "narrow": { + 1: "Я", + 2: "Ф", + 3: "М", + 4: "А", + 5: "М", + 6: "И", + 7: "И", + 8: "А", + 9: "С", + 10: "О", + 11: "Н", + 12: "Д", }, - 'wide': { - 1: 'января', - 2: 'февраля', - 3: 'марта', - 4: 'апреля', - 5: 'мая', - 6: 'июня', - 7: 'июля', - 8: 'августа', - 9: 'сентября', - 10: 'октября', - 11: 'ноября', - 12: 'декабря', + "wide": { + 1: "января", + 2: "февраля", + 3: "марта", + 4: "апреля", + 5: "мая", + 6: "июня", + 7: "июля", + 8: "августа", + 9: "сентября", + 10: "октября", + 11: "ноября", + 12: "декабря", }, }, - 'units': { - 'year': { - 'one': '{0} год', - 'few': '{0} года', - 'many': '{0} лет', - 'other': '{0} года', + "units": { + "year": { + "one": "{0} год", + "few": "{0} года", + "many": "{0} лет", + "other": "{0} года", }, - 'month': { - 'one': '{0} месяц', - 'few': '{0} месяца', - 'many': '{0} месяцев', - 'other': '{0} месяца', + "month": { + "one": "{0} месяц", + "few": "{0} месяца", + "many": "{0} месяцев", + "other": "{0} месяца", }, - 'week': { - 'one': '{0} неделя', - 'few': '{0} недели', - 'many': '{0} недель', - 'other': '{0} недели', + "week": { + "one": "{0} неделя", + "few": "{0} недели", + "many": "{0} недель", + "other": "{0} недели", }, - 'day': { - 'one': '{0} день', - 'few': '{0} дня', - 'many': '{0} дней', - 'other': '{0} дня', + "day": { + "one": "{0} день", + "few": "{0} дня", + "many": "{0} дней", + "other": "{0} дня", }, - 'hour': { - 'one': '{0} час', - 'few': '{0} часа', - 'many': '{0} часов', - 'other': '{0} часа', + "hour": { + "one": "{0} час", + "few": "{0} часа", + "many": "{0} часов", + "other": "{0} часа", }, - 'minute': { - 'one': '{0} минута', - 'few': '{0} минуты', - 'many': '{0} минут', - 'other': '{0} минуты', + "minute": { + "one": "{0} минута", + "few": "{0} минуты", + "many": "{0} минут", + "other": "{0} минуты", }, - 'second': { - 'one': '{0} секунда', - 'few': '{0} секунды', - 'many': '{0} секунд', - 'other': '{0} секунды', + "second": { + "one": "{0} секунда", + "few": "{0} секунды", + "many": "{0} секунд", + "other": "{0} секунды", }, - 'microsecond': { - 'one': '{0} микросекунда', - 'few': '{0} микросекунды', - 'many': '{0} микросекунд', - 'other': '{0} микросекунды', + "microsecond": { + "one": "{0} микросекунда", + "few": "{0} микросекунды", + "many": "{0} микросекунд", + "other": "{0} микросекунды", }, }, - 'relative': { - 'year': { - 'future': { - 'other': 'через {0} года', - 'one': 'через {0} год', - 'few': 'через {0} года', - 'many': 'через {0} лет', + "relative": { + "year": { + "future": { + "other": "через {0} года", + "one": "через {0} год", + "few": "через {0} года", + "many": "через {0} лет", }, - 'past': { - 'other': '{0} года назад', - 'one': '{0} год назад', - 'few': '{0} года назад', - 'many': '{0} лет назад', + "past": { + "other": "{0} года назад", + "one": "{0} год назад", + "few": "{0} года назад", + "many": "{0} лет назад", }, }, - 'month': { - 'future': { - 'other': 'через {0} месяца', - 'one': 'через {0} месяц', - 'few': 'через {0} месяца', - 'many': 'через {0} месяцев', + "month": { + "future": { + "other": "через {0} месяца", + "one": "через {0} месяц", + "few": "через {0} месяца", + "many": "через {0} месяцев", }, - 'past': { - 'other': '{0} месяца назад', - 'one': '{0} месяц назад', - 'few': '{0} месяца назад', - 'many': '{0} месяцев назад', + "past": { + "other": "{0} месяца назад", + "one": "{0} месяц назад", + "few": "{0} месяца назад", + "many": "{0} месяцев назад", }, }, - 'week': { - 'future': { - 'other': 'через {0} недели', - 'one': 'через {0} неделю', - 'few': 'через {0} недели', - 'many': 'через {0} недель', + "week": { + "future": { + "other": "через {0} недели", + "one": "через {0} неделю", + "few": "через {0} недели", + "many": "через {0} недель", }, - 'past': { - 'other': '{0} недели назад', - 'one': '{0} неделю назад', - 'few': '{0} недели назад', - 'many': '{0} недель назад', + "past": { + "other": "{0} недели назад", + "one": "{0} неделю назад", + "few": "{0} недели назад", + "many": "{0} недель назад", }, }, - 'day': { - 'future': { - 'other': 'через {0} дня', - 'one': 'через {0} день', - 'few': 'через {0} дня', - 'many': 'через {0} дней', + "day": { + "future": { + "other": "через {0} дня", + "one": "через {0} день", + "few": "через {0} дня", + "many": "через {0} дней", }, - 'past': { - 'other': '{0} дня назад', - 'one': '{0} день назад', - 'few': '{0} дня назад', - 'many': '{0} дней назад', + "past": { + "other": "{0} дня назад", + "one": "{0} день назад", + "few": "{0} дня назад", + "many": "{0} дней назад", }, }, - 'hour': { - 'future': { - 'other': 'через {0} часа', - 'one': 'через {0} час', - 'few': 'через {0} часа', - 'many': 'через {0} часов', + "hour": { + "future": { + "other": "через {0} часа", + "one": "через {0} час", + "few": "через {0} часа", + "many": "через {0} часов", }, - 'past': { - 'other': '{0} часа назад', - 'one': '{0} час назад', - 'few': '{0} часа назад', - 'many': '{0} часов назад', + "past": { + "other": "{0} часа назад", + "one": "{0} час назад", + "few": "{0} часа назад", + "many": "{0} часов назад", }, }, - 'minute': { - 'future': { - 'other': 'через {0} минуты', - 'one': 'через {0} минуту', - 'few': 'через {0} минуты', - 'many': 'через {0} минут', + "minute": { + "future": { + "other": "через {0} минуты", + "one": "через {0} минуту", + "few": "через {0} минуты", + "many": "через {0} минут", }, - 'past': { - 'other': '{0} минуты назад', - 'one': '{0} минуту назад', - 'few': '{0} минуты назад', - 'many': '{0} минут назад', + "past": { + "other": "{0} минуты назад", + "one": "{0} минуту назад", + "few": "{0} минуты назад", + "many": "{0} минут назад", }, }, - 'second': { - 'future': { - 'other': 'через {0} секунды', - 'one': 'через {0} секунду', - 'few': 'через {0} секунды', - 'many': 'через {0} секунд', + "second": { + "future": { + "other": "через {0} секунды", + "one": "через {0} секунду", + "few": "через {0} секунды", + "many": "через {0} секунд", }, - 'past': { - 'other': '{0} секунды назад', - 'one': '{0} секунду назад', - 'few': '{0} секунды назад', - 'many': '{0} секунд назад', + "past": { + "other": "{0} секунды назад", + "one": "{0} секунду назад", + "few": "{0} секунды назад", + "many": "{0} секунд назад", }, }, }, - 'day_periods': { - 'midnight': 'полночь', - 'am': 'AM', - 'noon': 'полдень', - 'pm': 'PM', - 'morning1': 'утра', - 'afternoon1': 'дня', - 'evening1': 'вечера', - 'night1': 'ночи', + "day_periods": { + "midnight": "полночь", + "am": "AM", + "noon": "полдень", + "pm": "PM", + "morning1": "утра", + "afternoon1": "дня", + "evening1": "вечера", + "night1": "ночи", }, }, - 'custom': custom_translations + "custom": custom_translations, } diff --git a/pyproject.toml b/pyproject.toml index 0449aa99..713388e4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "pendulum" -version = "2.0.4" +version = "2.0.5" description = "Python datetimes made easy" authors = ["Sébastien Eustace "] license = "MIT" @@ -31,7 +31,7 @@ pytz = ">=2018.3" babel = "^2.5" cleo = "^0.6.1" tox = "^3.0" -black = { version = "^18.4-alpha.0", python = "^3.6" } +black = { version = "^19.3b0", python = "^3.6" } pre-commit = "^1.10" diff --git a/tests/localization/test_ru.py b/tests/localization/test_ru.py index e5003f14..c257f118 100644 --- a/tests/localization/test_ru.py +++ b/tests/localization/test_ru.py @@ -4,7 +4,7 @@ import pendulum -locale = 'ru' +locale = "ru" def test_diff_for_humans(): @@ -14,75 +14,75 @@ def test_diff_for_humans(): def diff_for_humans(): d = pendulum.now().subtract(seconds=1) - assert d.diff_for_humans(locale=locale) == '1 секунду назад' + assert d.diff_for_humans(locale=locale) == "1 секунду назад" d = pendulum.now().subtract(seconds=2) - assert d.diff_for_humans(locale=locale) == '2 секунды назад' + assert d.diff_for_humans(locale=locale) == "2 секунды назад" d = pendulum.now().subtract(seconds=5) - assert d.diff_for_humans(locale=locale) == '5 секунд назад' + assert d.diff_for_humans(locale=locale) == "5 секунд назад" d = pendulum.now().subtract(seconds=21) - assert d.diff_for_humans(locale=locale) == '21 секунду назад' + assert d.diff_for_humans(locale=locale) == "21 секунду назад" d = pendulum.now().subtract(minutes=1) - assert d.diff_for_humans(locale=locale) == '1 минуту назад' + assert d.diff_for_humans(locale=locale) == "1 минуту назад" d = pendulum.now().subtract(minutes=2) - assert d.diff_for_humans(locale=locale) == '2 минуты назад' + assert d.diff_for_humans(locale=locale) == "2 минуты назад" d = pendulum.now().subtract(minutes=5) - assert d.diff_for_humans(locale=locale) == '5 минут назад' + assert d.diff_for_humans(locale=locale) == "5 минут назад" d = pendulum.now().subtract(hours=1) - assert d.diff_for_humans(locale=locale) == '1 час назад' + assert d.diff_for_humans(locale=locale) == "1 час назад" d = pendulum.now().subtract(hours=2) - assert d.diff_for_humans(locale=locale) == '2 часа назад' + assert d.diff_for_humans(locale=locale) == "2 часа назад" d = pendulum.now().subtract(hours=5) - assert d.diff_for_humans(locale=locale) == '5 часов назад' + assert d.diff_for_humans(locale=locale) == "5 часов назад" d = pendulum.now().subtract(days=1) - assert d.diff_for_humans(locale=locale) == '1 день назад' + assert d.diff_for_humans(locale=locale) == "1 день назад" d = pendulum.now().subtract(days=2) - assert d.diff_for_humans(locale=locale) == '2 дня назад' + assert d.diff_for_humans(locale=locale) == "2 дня назад" d = pendulum.now().subtract(days=5) - assert d.diff_for_humans(locale=locale) == '5 дней назад' + assert d.diff_for_humans(locale=locale) == "5 дней назад" d = pendulum.now().subtract(weeks=1) - assert d.diff_for_humans(locale=locale) == '1 неделю назад' + assert d.diff_for_humans(locale=locale) == "1 неделю назад" d = pendulum.now().subtract(weeks=2) - assert d.diff_for_humans(locale=locale) == '2 недели назад' + assert d.diff_for_humans(locale=locale) == "2 недели назад" d = pendulum.now().subtract(months=1) - assert d.diff_for_humans(locale=locale) == '1 месяц назад' + assert d.diff_for_humans(locale=locale) == "1 месяц назад" d = pendulum.now().subtract(months=2) - assert d.diff_for_humans(locale=locale) == '2 месяца назад' + assert d.diff_for_humans(locale=locale) == "2 месяца назад" d = pendulum.now().subtract(months=5) - assert d.diff_for_humans(locale=locale) == '5 месяцев назад' + assert d.diff_for_humans(locale=locale) == "5 месяцев назад" d = pendulum.now().subtract(years=1) - assert d.diff_for_humans(locale=locale) == '1 год назад' + assert d.diff_for_humans(locale=locale) == "1 год назад" d = pendulum.now().subtract(years=2) - assert d.diff_for_humans(locale=locale) == '2 года назад' + assert d.diff_for_humans(locale=locale) == "2 года назад" d = pendulum.now().subtract(years=5) - assert d.diff_for_humans(locale=locale) == '5 лет назад' + assert d.diff_for_humans(locale=locale) == "5 лет назад" d = pendulum.now().add(seconds=1) - assert d.diff_for_humans(locale=locale) == 'через 1 секунду' + assert d.diff_for_humans(locale=locale) == "через 1 секунду" d = pendulum.now().add(seconds=1) d2 = pendulum.now() - assert d.diff_for_humans(d2, locale=locale) == '1 секунда после' - assert d2.diff_for_humans(d, locale=locale) == '1 секунда до' + assert d.diff_for_humans(d2, locale=locale) == "1 секунда после" + assert d2.diff_for_humans(d, locale=locale) == "1 секунда до" - assert d.diff_for_humans(d2, True, locale=locale) == '1 секунда' - assert d2.diff_for_humans(d.add(seconds=1), True, locale=locale) == '2 секунды' + assert d.diff_for_humans(d2, True, locale=locale) == "1 секунда" + assert d2.diff_for_humans(d.add(seconds=1), True, locale=locale) == "2 секунды" From c17eb8cd3894217b1ab8f16fc8b5192676796e69 Mon Sep 17 00:00:00 2001 From: Michele Comitini Date: Thu, 8 Aug 2019 14:06:24 +0200 Subject: [PATCH 034/256] Add italian [it] locale. --- pendulum/locales/it/__init__.py | 0 pendulum/locales/it/custom.py | 19 +++ pendulum/locales/it/locale.py | 216 ++++++++++++++++++++++++++++++++ 3 files changed, 235 insertions(+) create mode 100644 pendulum/locales/it/__init__.py create mode 100644 pendulum/locales/it/custom.py create mode 100644 pendulum/locales/it/locale.py diff --git a/pendulum/locales/it/__init__.py b/pendulum/locales/it/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pendulum/locales/it/custom.py b/pendulum/locales/it/custom.py new file mode 100644 index 00000000..89af8910 --- /dev/null +++ b/pendulum/locales/it/custom.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +""" +it custom locale file. +""" + +from __future__ import unicode_literals + + +translations = { + # Date formats + "date_formats": { + "LTS": "H:mm:ss", + "LT": "H:mm", + "L": "DD/MM/YYYY", + "LL": "D MMMM YYYY", + "LLL": "D MMMM YYYY [alle] H:mm", + "LLLL": "dddd, D MMMM YYYY [alle] H:mm", + }, +} diff --git a/pendulum/locales/it/locale.py b/pendulum/locales/it/locale.py new file mode 100644 index 00000000..08b2407b --- /dev/null +++ b/pendulum/locales/it/locale.py @@ -0,0 +1,216 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals +""" +it locale file. + +It has been generated automatically and must not be modified directly. +""" + +from .custom import translations as custom_translations + + +locale = { + 'plural': lambda n: 'one' if ((n == n and ((n == 1))) and (0 == 0 and ((0 == 0)))) else 'other', + 'ordinal': lambda n: 'many' if (n == n and ((n == 11) or (n == 8) or (n == 80) or (n == 800))) else 'other', + 'translations': { + 'days': { + 'abbreviated': { + 0: 'dom', + 1: 'lun', + 2: 'mar', + 3: 'mer', + 4: 'gio', + 5: 'ven', + 6: 'sab', + }, + 'narrow': { + 0: 'D', + 1: 'L', + 2: 'M', + 3: 'M', + 4: 'G', + 5: 'V', + 6: 'S', + }, + 'short': { + 0: 'dom', + 1: 'lun', + 2: 'mar', + 3: 'mer', + 4: 'gio', + 5: 'ven', + 6: 'sab', + }, + 'wide': { + 0: 'domenica', + 1: 'lunedì', + 2: 'martedì', + 3: 'mercoledì', + 4: 'giovedì', + 5: 'venerdì', + 6: 'sabato', + }, + }, + 'months': { + 'abbreviated': { + 1: 'gen', + 2: 'feb', + 3: 'mar', + 4: 'apr', + 5: 'mag', + 6: 'giu', + 7: 'lug', + 8: 'ago', + 9: 'set', + 10: 'ott', + 11: 'nov', + 12: 'dic', + }, + 'narrow': { + 1: 'G', + 2: 'F', + 3: 'M', + 4: 'A', + 5: 'M', + 6: 'G', + 7: 'L', + 8: 'A', + 9: 'S', + 10: 'O', + 11: 'N', + 12: 'D', + }, + 'wide': { + 1: 'gennaio', + 2: 'febbraio', + 3: 'marzo', + 4: 'aprile', + 5: 'maggio', + 6: 'giugno', + 7: 'luglio', + 8: 'agosto', + 9: 'settembre', + 10: 'ottobre', + 11: 'novembre', + 12: 'dicembre', + }, + }, + 'units': { + 'year': { + 'one': '{0} anno', + 'other': '{0} anni', + }, + 'month': { + 'one': '{0} mese', + 'other': '{0} mesi', + }, + 'week': { + 'one': '{0} settimana', + 'other': '{0} settimane', + }, + 'day': { + 'one': '{0} giorno', + 'other': '{0} giorni', + }, + 'hour': { + 'one': '{0} ora', + 'other': '{0} ore', + }, + 'minute': { + 'one': '{0} minuto', + 'other': '{0} minuti', + }, + 'second': { + 'one': '{0} secondo', + 'other': '{0} secondi', + }, + 'microsecond': { + 'one': '{0} microsecondo', + 'other': '{0} microsecondi', + }, + }, + 'relative': { + 'year': { + 'future': { + 'other': 'tra {0} anni', + 'one': 'tra {0} anno', + }, + 'past': { + 'other': '{0} anni fa', + 'one': '{0} anno fa', + }, + }, + 'month': { + 'future': { + 'other': 'tra {0} mesi', + 'one': 'tra {0} mese', + }, + 'past': { + 'other': '{0} mesi fa', + 'one': '{0} mese fa', + }, + }, + 'week': { + 'future': { + 'other': 'tra {0} settimane', + 'one': 'tra {0} settimana', + }, + 'past': { + 'other': '{0} settimane fa', + 'one': '{0} settimana fa', + }, + }, + 'day': { + 'future': { + 'other': 'tra {0} giorni', + 'one': 'tra {0} giorno', + }, + 'past': { + 'other': '{0} giorni fa', + 'one': '{0} giorno fa', + }, + }, + 'hour': { + 'future': { + 'other': 'tra {0} ore', + 'one': 'tra {0} ora', + }, + 'past': { + 'other': '{0} ore fa', + 'one': '{0} ora fa', + }, + }, + 'minute': { + 'future': { + 'other': 'tra {0} minuti', + 'one': 'tra {0} minuto', + }, + 'past': { + 'other': '{0} minuti fa', + 'one': '{0} minuto fa', + }, + }, + 'second': { + 'future': { + 'other': 'tra {0} secondi', + 'one': 'tra {0} secondo', + }, + 'past': { + 'other': '{0} secondi fa', + 'one': '{0} secondo fa', + }, + }, + }, + 'day_periods': { + 'midnight': 'mezzanotte', + 'am': 'AM', + 'noon': 'mezzogiorno', + 'pm': 'PM', + 'morning1': 'di mattina', + 'afternoon1': 'del pomeriggio', + 'evening1': 'di sera', + 'night1': 'di notte', + }, + }, + 'custom': custom_translations +} From fac17dc6284a8604870747be66d76677776a716b Mon Sep 17 00:00:00 2001 From: Kyle Altendorf Date: Sat, 10 Aug 2019 08:32:57 -0400 Subject: [PATCH 035/256] Reject text after matching string in from_format() (#372) * Reject text after matching string in from_format() * Let black have its way --- pendulum/formatting/formatter.py | 2 +- tests/datetime/test_from_format.py | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/pendulum/formatting/formatter.py b/pendulum/formatting/formatter.py index 76a4db4f..b41873ca 100644 --- a/pendulum/formatting/formatter.py +++ b/pendulum/formatting/formatter.py @@ -403,7 +403,7 @@ def parse( lambda m: self._replace_tokens(m.group(0), locale), escaped_fmt ) - if not re.match(pattern, time): + if not re.search("^" + pattern + "$", time): raise ValueError("String does not match format {}".format(fmt)) re.sub(pattern, lambda m: self._get_parsed_values(m, parsed, locale, now), time) diff --git a/tests/datetime/test_from_format.py b/tests/datetime/test_from_format.py index c5aeaa27..06404548 100644 --- a/tests/datetime/test_from_format.py +++ b/tests/datetime/test_from_format.py @@ -13,6 +13,11 @@ def test_from_format_returns_datetime(): assert "UTC" == d.timezone_name +def test_from_format_rejects_extra_text(): + with pytest.raises(ValueError): + pendulum.from_format("1975-05-21 22:32:11 extra text", "YYYY-MM-DD HH:mm:ss") + + def test_from_format_with_timezone_string(): d = pendulum.from_format( "1975-05-21 22:32:11", "YYYY-MM-DD HH:mm:ss", tz="Europe/London" From 2781f244fb6335be7366b448c23b0f7b6ae419ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Eustace?= Date: Sat, 10 Aug 2019 14:43:26 +0200 Subject: [PATCH 036/256] Use the latest version of Poetry --- .travis.yml | 3 ++- appveyor.yml | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 3ceaff15..e7baefe1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -66,7 +66,8 @@ install: source "$HOME/virtualenvs/pypy-$PYPY_VERSION/bin/activate" fi - pip install pip -U - - curl -sSL https://raw.githubusercontent.com/sdispater/poetry/master/get-poetry.py | python + - curl -fsS -o get-poetry.py https://raw.githubusercontent.com/sdispater/poetry/master/get-poetry.py + - python get-poetry.py --preview -y - source $HOME/.poetry/env - poetry install -v - poetry build -v diff --git a/appveyor.yml b/appveyor.yml index ba7c0c56..adf929b7 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -20,7 +20,7 @@ install: - "python -m pip install --disable-pip-version-check --user --upgrade pip" # Downloading and installing poetry - - python -m pip install poetry -U + - python -m pip install poetry>=1.0.0b1 -U # Install dependencies - python -m pip install codecov From fb6fc727c52e0abefc2c4bb859cf60fd572ceda0 Mon Sep 17 00:00:00 2001 From: Sutrisno Efendi Date: Sat, 10 Aug 2019 20:10:16 +0700 Subject: [PATCH 037/256] Add 'id' localization (#359) --- pendulum/locales/id/__init__.py | 0 pendulum/locales/id/custom.py | 22 +++++ pendulum/locales/id/locale.py | 143 ++++++++++++++++++++++++++++++++ tests/localization/test_id.py | 70 ++++++++++++++++ 4 files changed, 235 insertions(+) create mode 100644 pendulum/locales/id/__init__.py create mode 100644 pendulum/locales/id/custom.py create mode 100644 pendulum/locales/id/locale.py create mode 100644 tests/localization/test_id.py diff --git a/pendulum/locales/id/__init__.py b/pendulum/locales/id/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pendulum/locales/id/custom.py b/pendulum/locales/id/custom.py new file mode 100644 index 00000000..8fbeeb37 --- /dev/null +++ b/pendulum/locales/id/custom.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +""" +id custom locale file. +""" + +translations = { + "units": {"few_second": "beberapa detik"}, + "ago": "{} yang lalu", + "from_now": "dalam {}", + "after": "{0} kemudian", + "before": "{0} yang lalu", + "date_formats": { + "LTS": "HH:mm:ss", + "LT": "HH:mm", + "LLLL": "dddd [d.] D. MMMM YYYY HH:mm", + "LLL": "D. MMMM YYYY HH:mm", + "LL": "D. MMMM YYYY", + "L": "DD/MM/YYYY", + }, +} diff --git a/pendulum/locales/id/locale.py b/pendulum/locales/id/locale.py new file mode 100644 index 00000000..cb8c4b84 --- /dev/null +++ b/pendulum/locales/id/locale.py @@ -0,0 +1,143 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +""" +id locale file. + +It has been generated automatically and must not be modified directly. +""" + +from .custom import translations as custom_translations + + +locale = { + "plural": lambda n: "other", + "ordinal": lambda n: "other", + "translations": { + "days": { + "abbreviated": { + 0: "Min", + 1: "Sen", + 2: "Sel", + 3: "Rab", + 4: "Kam", + 5: "Jum", + 6: "Sab", + }, + "narrow": {0: "M", 1: "S", 2: "S", 3: "R", 4: "K", 5: "J", 6: "S"}, + "short": { + 0: "Min", + 1: "Sen", + 2: "Sel", + 3: "Rab", + 4: "Kam", + 5: "Jum", + 6: "Sab", + }, + "wide": { + 0: "Minggu", + 1: "Senin", + 2: "Selasa", + 3: "Rabu", + 4: "Kamis", + 5: "Jumat", + 6: "Sabtu", + }, + }, + "months": { + "abbreviated": { + 1: "Jan", + 2: "Feb", + 3: "Mar", + 4: "Apr", + 5: "Mei", + 6: "Jun", + 7: "Jul", + 8: "Agt", + 9: "Sep", + 10: "Okt", + 11: "Nov", + 12: "Des", + }, + "narrow": { + 1: "J", + 2: "F", + 3: "M", + 4: "A", + 5: "M", + 6: "J", + 7: "J", + 8: "A", + 9: "S", + 10: "O", + 11: "N", + 12: "D", + }, + "wide": { + 1: "Januari", + 2: "Februari", + 3: "Maret", + 4: "April", + 5: "Mei", + 6: "Juni", + 7: "Juli", + 8: "Agustus", + 9: "September", + 10: "Oktober", + 11: "November", + 12: "Desember", + }, + }, + "units": { + "year": {"other": "{0} tahun"}, + "month": {"other": "{0} bulan"}, + "week": {"other": "{0} minggu"}, + "day": {"other": "{0} hari"}, + "hour": {"other": "{0} jam"}, + "minute": {"other": "{0} menit"}, + "second": {"other": "{0} detik"}, + "microsecond": {"other": "{0} mikrodetik"}, + }, + "relative": { + "year": { + "future": {"other": "dalam {0} tahun"}, + "past": {"other": "{0} tahun yang lalu"}, + }, + "month": { + "future": {"other": "dalam {0} bulan"}, + "past": {"other": "{0} bulan yang lalu"}, + }, + "week": { + "future": {"other": "dalam {0} minggu"}, + "past": {"other": "{0} minggu yang lalu"}, + }, + "day": { + "future": {"other": "dalam {0} hari"}, + "past": {"other": "{0} hari yang lalu"}, + }, + "hour": { + "future": {"other": "dalam {0} jam"}, + "past": {"other": "{0} jam yang lalu"}, + }, + "minute": { + "future": {"other": "dalam {0} menit"}, + "past": {"other": "{0} menit yang lalu"}, + }, + "second": { + "future": {"other": "dalam {0} detik"}, + "past": {"other": "{0} detik yang lalu"}, + }, + }, + "day_periods": { + "midnight": "tengah malam", + "am": "AM", + "noon": "tengah hari", + "pm": "PM", + "morning1": "pagi", + "afternoon1": "siang", + "evening1": "sore", + "night1": "malam", + }, + }, + "custom": custom_translations, +} diff --git a/tests/localization/test_id.py b/tests/localization/test_id.py new file mode 100644 index 00000000..5b18a67a --- /dev/null +++ b/tests/localization/test_id.py @@ -0,0 +1,70 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +import pendulum + + +locale = "id" + + +def test_diff_for_humans(): + with pendulum.test(pendulum.datetime(2016, 8, 29)): + diff_for_humans() + + +def diff_for_humans(): + d = pendulum.now().subtract(seconds=1) + assert d.diff_for_humans(locale=locale) == "beberapa detik yang lalu" + + d = pendulum.now().subtract(seconds=2) + assert d.diff_for_humans(locale=locale) == "beberapa detik yang lalu" + + d = pendulum.now().subtract(seconds=21) + assert d.diff_for_humans(locale=locale) == "21 detik yang lalu" + + d = pendulum.now().subtract(minutes=1) + assert d.diff_for_humans(locale=locale) == "1 menit yang lalu" + + d = pendulum.now().subtract(minutes=2) + assert d.diff_for_humans(locale=locale) == "2 menit yang lalu" + + d = pendulum.now().subtract(hours=1) + assert d.diff_for_humans(locale=locale) == "1 jam yang lalu" + + d = pendulum.now().subtract(hours=2) + assert d.diff_for_humans(locale=locale) == "2 jam yang lalu" + + d = pendulum.now().subtract(days=1) + assert d.diff_for_humans(locale=locale) == "1 hari yang lalu" + + d = pendulum.now().subtract(days=2) + assert d.diff_for_humans(locale=locale) == "2 hari yang lalu" + + d = pendulum.now().subtract(weeks=1) + assert d.diff_for_humans(locale=locale) == "1 minggu yang lalu" + + d = pendulum.now().subtract(weeks=2) + assert d.diff_for_humans(locale=locale) == "2 minggu yang lalu" + + d = pendulum.now().subtract(months=1) + assert d.diff_for_humans(locale=locale) == "1 bulan yang lalu" + + d = pendulum.now().subtract(months=2) + assert d.diff_for_humans(locale=locale) == "2 bulan yang lalu" + + d = pendulum.now().subtract(years=1) + assert d.diff_for_humans(locale=locale) == "1 tahun yang lalu" + + d = pendulum.now().subtract(years=2) + assert d.diff_for_humans(locale=locale) == "2 tahun yang lalu" + + d = pendulum.now().add(seconds=1) + assert d.diff_for_humans(locale=locale) == "dalam beberapa detik" + + d = pendulum.now().add(seconds=1) + d2 = pendulum.now() + assert d.diff_for_humans(d2, locale=locale) == "beberapa detik kemudian" + assert d2.diff_for_humans(d, locale=locale) == "beberapa detik yang lalu" + + assert d.diff_for_humans(d2, True, locale=locale) == "beberapa detik" + assert d2.diff_for_humans(d.add(seconds=1), True, locale=locale) == "beberapa detik" From 84571440f09db06cd17211e8764c75e98de60c9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Eustace?= Date: Sat, 10 Aug 2019 15:41:38 +0200 Subject: [PATCH 038/256] Upgrade dependencies --- README.rst | 2 +- clock | 53 +++++++++++++++++++++++++++++++++----------------- pyproject.toml | 7 ++----- 3 files changed, 38 insertions(+), 24 deletions(-) diff --git a/README.rst b/README.rst index 70f59df8..313d4c92 100644 --- a/README.rst +++ b/README.rst @@ -203,7 +203,7 @@ If the locale does not exist you will need to create it by using the ``clock`` u .. code-block:: bash - ./clock locale:create + ./clock locale create It will generate a directory in ``pendulum/locales`` named after your locale, with the following structure: diff --git a/clock b/clock index df73ff7d..e562176c 100755 --- a/clock +++ b/clock @@ -17,6 +17,7 @@ from babel.dates import tokenize_pattern, PATTERN_CHARS from babel.localedata import load, normalize_locale, LocaleDataDict from cleo import Application, Command +from cleo import argument from pendulum import __version__ @@ -43,12 +44,11 @@ class _LambdaCompiler(_GettextCompiler): class LocaleCreate(Command): - """ - Creates locale translations. - locale:create - {locales?* : Locales to dump} - """ + name = "create" + description = "Creates locale translations." + + arguments = [argument("locales", "Locales to dump.", optional=False, multiple=True)] TEMPLATE = """# -*- coding: utf-8 -*- from __future__ import unicode_literals @@ -93,7 +93,7 @@ translations = {{}} normalized = normalize_locale(locale.replace("-", "_")) if not normalized: - self.error("Locale [{}] does not exist.".format(locale)) + self.line("Locale [{}] does not exist.".format(locale)) continue self.line("Generating {} locale.".format(locale)) @@ -229,11 +229,9 @@ translations = {{}} class LocaleRecreate(Command): - """ - Recreate existing locales. - locale:recreate - """ + name = "recreate" + description = "Recreate existing locales." def handle(self): # Listing locales @@ -245,12 +243,21 @@ class LocaleRecreate(Command): self.call("locale:create", [("locales", locales)]) +class LocaleCommand(Command): + + name = "locale" + description = "Locale related commands." + + commands = [LocaleCreate()] + + def handle(self): + self.call("help", self._config.name) + + class WindowsTzDump(Command): - """ - Dumps the mapping of Windows timezones to IANA timezones. - windows:tz:dump - """ + name = "dump-timezones" + description = "Dumps the mapping of Windows timezones to IANA timezones." MAPPING_DIR = os.path.join("pendulum", "tz", "data") @@ -269,10 +276,20 @@ class WindowsTzDump(Command): f.write(mapping) -app = Application("Clock", __version__) -app.add(LocaleCreate()) -app.add(LocaleRecreate()) -app.add(WindowsTzDump()) +class WindowsCommand(Command): + + name = "windows" + description = "Windows related commands." + + commands = [WindowsTzDump()] + + def handle(self): + self.call("help", self._config.name) + + +app = Application("clock", __version__) +app.add(LocaleCommand()) +app.add(WindowsCommand()) if __name__ == "__main__": diff --git a/pyproject.toml b/pyproject.toml index 713388e4..1bb8e101 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,13 +4,10 @@ version = "2.0.5" description = "Python datetimes made easy" authors = ["Sébastien Eustace "] license = "MIT" - readme = 'README.rst' - homepage = "https://pendulum.eustace.io" repository = "https://github.com/sdispater/pendulum" documentation = "https://pendulum.eustace.io/docs" - keywords = ['datetime', 'date', 'time'] build = "build.py" @@ -25,11 +22,11 @@ pytzdata = ">=2018.3" typing = { version = "^3.6", python = "<3.5" } [tool.poetry.dev-dependencies] -pytest = "^3.4" +pytest = "^4.6" pytest-cov = "^2.5" pytz = ">=2018.3" babel = "^2.5" -cleo = "^0.6.1" +cleo = "^0.7.5" tox = "^3.0" black = { version = "^19.3b0", python = "^3.6" } pre-commit = "^1.10" From 401d636acb77bf155e520912abfdbf8e91dd07ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Eustace?= Date: Sat, 10 Aug 2019 15:43:51 +0200 Subject: [PATCH 039/256] Update pyproject.toml --- pyproject.toml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 1bb8e101..34993096 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,6 +12,11 @@ keywords = ['datetime', 'date', 'time'] build = "build.py" +packages = [ + {include = "pendulum"}, + {include = "tests", format = "sdist"}, +] + [tool.poetry.dependencies] python = "~2.7 || ^3.4" @@ -33,5 +38,5 @@ pre-commit = "^1.10" [build-system] -requires = ["poetry>=0.12"] +requires = ["poetry>=1.0.0b1"] build-backend = "poetry.masonry.api" From bc32743b3d9a7d59cc005fc82a7842c5abff84b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Eustace?= Date: Sat, 10 Aug 2019 15:44:06 +0200 Subject: [PATCH 040/256] Update Windows timezone mapping --- pendulum/tz/data/windows.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/pendulum/tz/data/windows.py b/pendulum/tz/data/windows.py index de2ad84d..68bcfeb2 100644 --- a/pendulum/tz/data/windows.py +++ b/pendulum/tz/data/windows.py @@ -3,16 +3,21 @@ "AUS Eastern Standard Time": "Australia/Sydney", "Afghanistan Standard Time": "Asia/Kabul", "Alaskan Standard Time": "America/Anchorage", + "Aleutian Standard Time": "America/Adak", + "Altai Standard Time": "Asia/Barnaul", "Arab Standard Time": "Asia/Riyadh", "Arabian Standard Time": "Asia/Dubai", "Arabic Standard Time": "Asia/Baghdad", "Argentina Standard Time": "America/Buenos_Aires", + "Astrakhan Standard Time": "Europe/Astrakhan", "Atlantic Standard Time": "America/Halifax", + "Aus Central W. Standard Time": "Australia/Eucla", "Azerbaijan Standard Time": "Asia/Baku", "Azores Standard Time": "Atlantic/Azores", "Bahia Standard Time": "America/Bahia", "Bangladesh Standard Time": "Asia/Dhaka", "Belarus Standard Time": "Europe/Minsk", + "Bougainville Standard Time": "Pacific/Bougainville", "Canada Central Standard Time": "America/Regina", "Cape Verde Standard Time": "Atlantic/Cape_Verde", "Caucasus Standard Time": "Asia/Yerevan", @@ -25,12 +30,15 @@ "Central Pacific Standard Time": "Pacific/Guadalcanal", "Central Standard Time": "America/Chicago", "Central Standard Time (Mexico)": "America/Mexico_City", + "Chatham Islands Standard Time": "Pacific/Chatham", "China Standard Time": "Asia/Shanghai", + "Cuba Standard Time": "America/Havana", "Dateline Standard Time": "Etc/GMT+12", "E. Africa Standard Time": "Africa/Nairobi", "E. Australia Standard Time": "Australia/Brisbane", "E. Europe Standard Time": "Europe/Chisinau", "E. South America Standard Time": "America/Sao_Paulo", + "Easter Island Standard Time": "Pacific/Easter", "Eastern Standard Time": "America/New_York", "Eastern Standard Time (Mexico)": "America/Cancun", "Egypt Standard Time": "Africa/Cairo", @@ -42,6 +50,7 @@ "Georgian Standard Time": "Asia/Tbilisi", "Greenland Standard Time": "America/Godthab", "Greenwich Standard Time": "Atlantic/Reykjavik", + "Haiti Standard Time": "America/Port-au-Prince", "Hawaiian Standard Time": "Pacific/Honolulu", "India Standard Time": "Asia/Calcutta", "Iran Standard Time": "Asia/Tehran", @@ -51,7 +60,10 @@ "Korea Standard Time": "Asia/Seoul", "Libya Standard Time": "Africa/Tripoli", "Line Islands Standard Time": "Pacific/Kiritimati", + "Lord Howe Standard Time": "Australia/Lord_Howe", "Magadan Standard Time": "Asia/Magadan", + "Magallanes Standard Time": "America/Punta_Arenas", + "Marquesas Standard Time": "Pacific/Marquesas", "Mauritius Standard Time": "Indian/Mauritius", "Middle East Standard Time": "Asia/Beirut", "Montevideo Standard Time": "America/Montevideo", @@ -64,11 +76,14 @@ "Nepal Standard Time": "Asia/Katmandu", "New Zealand Standard Time": "Pacific/Auckland", "Newfoundland Standard Time": "America/St_Johns", + "Norfolk Standard Time": "Pacific/Norfolk", "North Asia East Standard Time": "Asia/Irkutsk", "North Asia Standard Time": "Asia/Krasnoyarsk", "North Korea Standard Time": "Asia/Pyongyang", + "Omsk Standard Time": "Asia/Omsk", "Pacific SA Standard Time": "America/Santiago", "Pacific Standard Time": "America/Los_Angeles", + "Pacific Standard Time (Mexico)": "America/Tijuana", "Pakistan Standard Time": "Asia/Karachi", "Paraguay Standard Time": "America/Asuncion", "Romance Standard Time": "Europe/Paris", @@ -80,21 +95,33 @@ "SA Pacific Standard Time": "America/Bogota", "SA Western Standard Time": "America/La_Paz", "SE Asia Standard Time": "Asia/Bangkok", + "Saint Pierre Standard Time": "America/Miquelon", + "Sakhalin Standard Time": "Asia/Sakhalin", "Samoa Standard Time": "Pacific/Apia", + "Sao Tome Standard Time": "Africa/Sao_Tome", + "Saratov Standard Time": "Europe/Saratov", "Singapore Standard Time": "Asia/Singapore", "South Africa Standard Time": "Africa/Johannesburg", "Sri Lanka Standard Time": "Asia/Colombo", + "Sudan Standard Time": "Africa/Khartoum", "Syria Standard Time": "Asia/Damascus", "Taipei Standard Time": "Asia/Taipei", "Tasmania Standard Time": "Australia/Hobart", + "Tocantins Standard Time": "America/Araguaina", "Tokyo Standard Time": "Asia/Tokyo", + "Tomsk Standard Time": "Asia/Tomsk", "Tonga Standard Time": "Pacific/Tongatapu", + "Transbaikal Standard Time": "Asia/Chita", "Turkey Standard Time": "Europe/Istanbul", + "Turks And Caicos Standard Time": "America/Grand_Turk", "US Eastern Standard Time": "America/Indianapolis", "US Mountain Standard Time": "America/Phoenix", "UTC": "Etc/GMT", "UTC+12": "Etc/GMT-12", + "UTC+13": "Etc/GMT-13", "UTC-02": "Etc/GMT+2", + "UTC-08": "Etc/GMT+8", + "UTC-09": "Etc/GMT+9", "UTC-11": "Etc/GMT+11", "Ulaanbaatar Standard Time": "Asia/Ulaanbaatar", "Venezuela Standard Time": "America/Caracas", @@ -102,7 +129,9 @@ "W. Australia Standard Time": "Australia/Perth", "W. Central Africa Standard Time": "Africa/Lagos", "W. Europe Standard Time": "Europe/Berlin", + "W. Mongolia Standard Time": "Asia/Hovd", "West Asia Standard Time": "Asia/Tashkent", + "West Bank Standard Time": "Asia/Hebron", "West Pacific Standard Time": "Pacific/Port_Moresby", "Yakutsk Standard Time": "Asia/Yakutsk", } From 4bf1bf1e79388bfdde11436f74ab58a78c0036e6 Mon Sep 17 00:00:00 2001 From: Michele Comitini Date: Sat, 10 Aug 2019 22:25:41 +0200 Subject: [PATCH 041/256] Added tests. --- pendulum/locales/it/custom.py | 8 ++++ tests/localization/test_it.py | 89 +++++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+) create mode 100644 tests/localization/test_it.py diff --git a/pendulum/locales/it/custom.py b/pendulum/locales/it/custom.py index 89af8910..6f3963e1 100644 --- a/pendulum/locales/it/custom.py +++ b/pendulum/locales/it/custom.py @@ -7,6 +7,14 @@ translations = { + "units": {"few_second": "alcuni secondi"}, + # Relative Time + "ago": "{0} fa", + "from_now": "in {0}", + "after": "{0} dopo", + "before": "{0} prima", + # Ordinals + "ordinal": {"other": "°"}, # Date formats "date_formats": { "LTS": "H:mm:ss", diff --git a/tests/localization/test_it.py b/tests/localization/test_it.py new file mode 100644 index 00000000..3660a7a7 --- /dev/null +++ b/tests/localization/test_it.py @@ -0,0 +1,89 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +import pendulum + + +locale = "it" + + +def test_diff_for_humans(): + with pendulum.test(pendulum.datetime(2016, 8, 29)): + diff_for_humans() + + +def diff_for_humans(): + d = pendulum.now().subtract(seconds=1) + assert d.diff_for_humans(locale=locale) == "alcuni secondi fa" + + d = pendulum.now().subtract(seconds=2) + assert d.diff_for_humans(locale=locale) == "alcuni secondi fa" + + d = pendulum.now().subtract(minutes=1) + assert d.diff_for_humans(locale=locale) == "1 minuto fa" + + d = pendulum.now().subtract(minutes=2) + assert d.diff_for_humans(locale=locale) == "2 minuti fa" + + d = pendulum.now().subtract(hours=1) + assert d.diff_for_humans(locale=locale) == "1 ora fa" + + d = pendulum.now().subtract(hours=2) + assert d.diff_for_humans(locale=locale) == "2 ore fa" + + d = pendulum.now().subtract(days=1) + assert d.diff_for_humans(locale=locale) == "1 giorno fa" + + d = pendulum.now().subtract(days=2) + assert d.diff_for_humans(locale=locale) == "2 giorni fa" + + d = pendulum.now().subtract(weeks=1) + assert d.diff_for_humans(locale=locale) == "1 settimana fa" + + d = pendulum.now().subtract(weeks=2) + assert d.diff_for_humans(locale=locale) == "2 settimane fa" + + d = pendulum.now().subtract(months=1) + assert d.diff_for_humans(locale=locale) == "1 mese fa" + + d = pendulum.now().subtract(months=2) + assert d.diff_for_humans(locale=locale) == "2 mesi fa" + + d = pendulum.now().subtract(years=1) + assert d.diff_for_humans(locale=locale) == "1 anno fa" + + d = pendulum.now().subtract(years=2) + assert d.diff_for_humans(locale=locale) == "2 anni fa" + + d = pendulum.now().add(seconds=1) + assert d.diff_for_humans(locale=locale) == "in alcuni secondi" + + d = pendulum.now().add(seconds=1) + d2 = pendulum.now() + assert d.diff_for_humans(d2, locale=locale) == "alcuni secondi dopo" + assert d2.diff_for_humans(d, locale=locale) == "alcuni secondi prima" + + assert d.diff_for_humans(d2, True, locale=locale) == "alcuni secondi" + assert ( + d2.diff_for_humans(d.add(seconds=1), True, locale=locale) == "alcuni secondi" + ) + + +def test_format(): + d = pendulum.datetime(2016, 8, 28, 7, 3, 6, 123456) + assert d.format("dddd", locale=locale) == "domenica" + assert d.format("ddd", locale=locale) == "dom" + assert d.format("MMMM", locale=locale) == "agosto" + assert d.format("MMM", locale=locale) == "ago" + assert d.format("A", locale=locale) == "AM" + + assert d.format("LT", locale=locale) == "7:03" + assert d.format("LTS", locale=locale) == "7:03:06" + assert d.format("L", locale=locale) == "28/08/2016" + assert d.format("LL", locale=locale) == "28 agosto 2016" + assert d.format("LLL", locale=locale) == "28 agosto 2016 alle 7:03" + assert d.format("LLLL", locale=locale) == "domenica, 28 agosto 2016 alle 7:03" + + assert d.format("Do", locale=locale) == "28°" + d = pendulum.datetime(2019, 1, 1, 7, 3, 6, 123456) + assert d.format("Do", locale=locale) == "1°" From b89ca534ea479d9260bbb50d8c58bbf86cdcd05b Mon Sep 17 00:00:00 2001 From: Michele Comitini Date: Sat, 10 Aug 2019 23:09:15 +0200 Subject: [PATCH 042/256] blacken buffer --- tests/localization/test_it.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/localization/test_it.py b/tests/localization/test_it.py index 3660a7a7..6db66c99 100644 --- a/tests/localization/test_it.py +++ b/tests/localization/test_it.py @@ -64,9 +64,7 @@ def diff_for_humans(): assert d2.diff_for_humans(d, locale=locale) == "alcuni secondi prima" assert d.diff_for_humans(d2, True, locale=locale) == "alcuni secondi" - assert ( - d2.diff_for_humans(d.add(seconds=1), True, locale=locale) == "alcuni secondi" - ) + assert d2.diff_for_humans(d.add(seconds=1), True, locale=locale) == "alcuni secondi" def test_format(): From 090ff7a2f0a30d09edfecfd2497bcfb6b2c0b11b Mon Sep 17 00:00:00 2001 From: Michele Comitini Date: Sat, 10 Aug 2019 23:11:33 +0200 Subject: [PATCH 043/256] blacken also locale.py --- pendulum/locales/it/locale.py | 307 +++++++++++++--------------------- 1 file changed, 119 insertions(+), 188 deletions(-) diff --git a/pendulum/locales/it/locale.py b/pendulum/locales/it/locale.py index 08b2407b..5f31c209 100644 --- a/pendulum/locales/it/locale.py +++ b/pendulum/locales/it/locale.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals + """ it locale file. @@ -10,207 +11,137 @@ locale = { - 'plural': lambda n: 'one' if ((n == n and ((n == 1))) and (0 == 0 and ((0 == 0)))) else 'other', - 'ordinal': lambda n: 'many' if (n == n and ((n == 11) or (n == 8) or (n == 80) or (n == 800))) else 'other', - 'translations': { - 'days': { - 'abbreviated': { - 0: 'dom', - 1: 'lun', - 2: 'mar', - 3: 'mer', - 4: 'gio', - 5: 'ven', - 6: 'sab', - }, - 'narrow': { - 0: 'D', - 1: 'L', - 2: 'M', - 3: 'M', - 4: 'G', - 5: 'V', - 6: 'S', - }, - 'short': { - 0: 'dom', - 1: 'lun', - 2: 'mar', - 3: 'mer', - 4: 'gio', - 5: 'ven', - 6: 'sab', - }, - 'wide': { - 0: 'domenica', - 1: 'lunedì', - 2: 'martedì', - 3: 'mercoledì', - 4: 'giovedì', - 5: 'venerdì', - 6: 'sabato', + "plural": lambda n: "one" + if ((n == n and ((n == 1))) and (0 == 0 and ((0 == 0)))) + else "other", + "ordinal": lambda n: "many" + if (n == n and ((n == 11) or (n == 8) or (n == 80) or (n == 800))) + else "other", + "translations": { + "days": { + "abbreviated": { + 0: "dom", + 1: "lun", + 2: "mar", + 3: "mer", + 4: "gio", + 5: "ven", + 6: "sab", + }, + "narrow": {0: "D", 1: "L", 2: "M", 3: "M", 4: "G", 5: "V", 6: "S"}, + "short": { + 0: "dom", + 1: "lun", + 2: "mar", + 3: "mer", + 4: "gio", + 5: "ven", + 6: "sab", + }, + "wide": { + 0: "domenica", + 1: "lunedì", + 2: "martedì", + 3: "mercoledì", + 4: "giovedì", + 5: "venerdì", + 6: "sabato", }, }, - 'months': { - 'abbreviated': { - 1: 'gen', - 2: 'feb', - 3: 'mar', - 4: 'apr', - 5: 'mag', - 6: 'giu', - 7: 'lug', - 8: 'ago', - 9: 'set', - 10: 'ott', - 11: 'nov', - 12: 'dic', - }, - 'narrow': { - 1: 'G', - 2: 'F', - 3: 'M', - 4: 'A', - 5: 'M', - 6: 'G', - 7: 'L', - 8: 'A', - 9: 'S', - 10: 'O', - 11: 'N', - 12: 'D', - }, - 'wide': { - 1: 'gennaio', - 2: 'febbraio', - 3: 'marzo', - 4: 'aprile', - 5: 'maggio', - 6: 'giugno', - 7: 'luglio', - 8: 'agosto', - 9: 'settembre', - 10: 'ottobre', - 11: 'novembre', - 12: 'dicembre', + "months": { + "abbreviated": { + 1: "gen", + 2: "feb", + 3: "mar", + 4: "apr", + 5: "mag", + 6: "giu", + 7: "lug", + 8: "ago", + 9: "set", + 10: "ott", + 11: "nov", + 12: "dic", + }, + "narrow": { + 1: "G", + 2: "F", + 3: "M", + 4: "A", + 5: "M", + 6: "G", + 7: "L", + 8: "A", + 9: "S", + 10: "O", + 11: "N", + 12: "D", + }, + "wide": { + 1: "gennaio", + 2: "febbraio", + 3: "marzo", + 4: "aprile", + 5: "maggio", + 6: "giugno", + 7: "luglio", + 8: "agosto", + 9: "settembre", + 10: "ottobre", + 11: "novembre", + 12: "dicembre", }, }, - 'units': { - 'year': { - 'one': '{0} anno', - 'other': '{0} anni', - }, - 'month': { - 'one': '{0} mese', - 'other': '{0} mesi', - }, - 'week': { - 'one': '{0} settimana', - 'other': '{0} settimane', - }, - 'day': { - 'one': '{0} giorno', - 'other': '{0} giorni', - }, - 'hour': { - 'one': '{0} ora', - 'other': '{0} ore', - }, - 'minute': { - 'one': '{0} minuto', - 'other': '{0} minuti', - }, - 'second': { - 'one': '{0} secondo', - 'other': '{0} secondi', - }, - 'microsecond': { - 'one': '{0} microsecondo', - 'other': '{0} microsecondi', - }, + "units": { + "year": {"one": "{0} anno", "other": "{0} anni"}, + "month": {"one": "{0} mese", "other": "{0} mesi"}, + "week": {"one": "{0} settimana", "other": "{0} settimane"}, + "day": {"one": "{0} giorno", "other": "{0} giorni"}, + "hour": {"one": "{0} ora", "other": "{0} ore"}, + "minute": {"one": "{0} minuto", "other": "{0} minuti"}, + "second": {"one": "{0} secondo", "other": "{0} secondi"}, + "microsecond": {"one": "{0} microsecondo", "other": "{0} microsecondi"}, }, - 'relative': { - 'year': { - 'future': { - 'other': 'tra {0} anni', - 'one': 'tra {0} anno', - }, - 'past': { - 'other': '{0} anni fa', - 'one': '{0} anno fa', - }, + "relative": { + "year": { + "future": {"other": "tra {0} anni", "one": "tra {0} anno"}, + "past": {"other": "{0} anni fa", "one": "{0} anno fa"}, }, - 'month': { - 'future': { - 'other': 'tra {0} mesi', - 'one': 'tra {0} mese', - }, - 'past': { - 'other': '{0} mesi fa', - 'one': '{0} mese fa', - }, + "month": { + "future": {"other": "tra {0} mesi", "one": "tra {0} mese"}, + "past": {"other": "{0} mesi fa", "one": "{0} mese fa"}, }, - 'week': { - 'future': { - 'other': 'tra {0} settimane', - 'one': 'tra {0} settimana', - }, - 'past': { - 'other': '{0} settimane fa', - 'one': '{0} settimana fa', - }, + "week": { + "future": {"other": "tra {0} settimane", "one": "tra {0} settimana"}, + "past": {"other": "{0} settimane fa", "one": "{0} settimana fa"}, }, - 'day': { - 'future': { - 'other': 'tra {0} giorni', - 'one': 'tra {0} giorno', - }, - 'past': { - 'other': '{0} giorni fa', - 'one': '{0} giorno fa', - }, + "day": { + "future": {"other": "tra {0} giorni", "one": "tra {0} giorno"}, + "past": {"other": "{0} giorni fa", "one": "{0} giorno fa"}, }, - 'hour': { - 'future': { - 'other': 'tra {0} ore', - 'one': 'tra {0} ora', - }, - 'past': { - 'other': '{0} ore fa', - 'one': '{0} ora fa', - }, + "hour": { + "future": {"other": "tra {0} ore", "one": "tra {0} ora"}, + "past": {"other": "{0} ore fa", "one": "{0} ora fa"}, }, - 'minute': { - 'future': { - 'other': 'tra {0} minuti', - 'one': 'tra {0} minuto', - }, - 'past': { - 'other': '{0} minuti fa', - 'one': '{0} minuto fa', - }, + "minute": { + "future": {"other": "tra {0} minuti", "one": "tra {0} minuto"}, + "past": {"other": "{0} minuti fa", "one": "{0} minuto fa"}, }, - 'second': { - 'future': { - 'other': 'tra {0} secondi', - 'one': 'tra {0} secondo', - }, - 'past': { - 'other': '{0} secondi fa', - 'one': '{0} secondo fa', - }, + "second": { + "future": {"other": "tra {0} secondi", "one": "tra {0} secondo"}, + "past": {"other": "{0} secondi fa", "one": "{0} secondo fa"}, }, }, - 'day_periods': { - 'midnight': 'mezzanotte', - 'am': 'AM', - 'noon': 'mezzogiorno', - 'pm': 'PM', - 'morning1': 'di mattina', - 'afternoon1': 'del pomeriggio', - 'evening1': 'di sera', - 'night1': 'di notte', + "day_periods": { + "midnight": "mezzanotte", + "am": "AM", + "noon": "mezzogiorno", + "pm": "PM", + "morning1": "di mattina", + "afternoon1": "del pomeriggio", + "evening1": "di sera", + "night1": "di notte", }, }, - 'custom': custom_translations + "custom": custom_translations, } From 80a60f344972470528c4c8c18ca4ae9a78df2992 Mon Sep 17 00:00:00 2001 From: Bryan Forbes Date: Fri, 30 Nov 2018 11:02:38 -0600 Subject: [PATCH 044/256] Update typing comments and add py.typed for PEP-561 compliance --- pendulum/__init__.py | 24 ++++----- pendulum/_extensions/helpers.py | 21 +++++--- pendulum/datetime.py | 26 +++++---- pendulum/formatting/difference_formatter.py | 7 ++- pendulum/formatting/formatter.py | 26 +++++---- pendulum/helpers.py | 59 ++++++++++++++++----- pendulum/locales/locale.py | 17 +++--- pendulum/parser.py | 3 +- pendulum/py.typed | 0 pendulum/tz/__init__.py | 8 +-- pendulum/tz/local_timezone.py | 6 +-- pendulum/tz/timezone.py | 57 +++++++++++--------- pendulum/tz/zoneinfo/__init__.py | 4 +- pendulum/tz/zoneinfo/posix_timezone.py | 20 +++---- pendulum/tz/zoneinfo/reader.py | 16 +++--- pendulum/tz/zoneinfo/timezone.py | 4 +- pendulum/tz/zoneinfo/transition.py | 8 +-- pyproject.toml | 1 + 18 files changed, 186 insertions(+), 121 deletions(-) create mode 100644 pendulum/py.typed diff --git a/pendulum/__init__.py b/pendulum/__init__.py index 2dcac7ac..d38b69bc 100644 --- a/pendulum/__init__.py +++ b/pendulum/__init__.py @@ -1,7 +1,7 @@ from __future__ import absolute_import import datetime as _datetime -from typing import Union +from typing import Union, Optional from .__version__ import __version__ @@ -59,7 +59,7 @@ SECONDS_PER_DAY, ) -_TEST_NOW = None +_TEST_NOW = None # type: Optional[DateTime] _LOCALE = "en" _WEEK_STARTS_AT = MONDAY _WEEK_ENDS_AT = SUNDAY @@ -68,7 +68,7 @@ def _safe_timezone(obj): - # type: (Union[str, int, float, _datetime.tzinfo]) -> _Timezone + # type: (Optional[Union[str, float, _datetime.tzinfo, _Timezone]]) -> _Timezone """ Creates a timezone instance from a string, Timezone, TimezoneInfo or integer offset. @@ -105,7 +105,7 @@ def datetime( minute=0, # type: int second=0, # type: int microsecond=0, # type: int - tz=UTC, # type: Union[str, _Timezone] + tz=UTC, # type: Optional[Union[str, float, _Timezone]] dst_rule=POST_TRANSITION, # type: str ): # type: (...) -> DateTime """ @@ -169,8 +169,8 @@ def time(hour, minute=0, second=0, microsecond=0): # type: (int, int, int, int) def instance( - dt, tz=UTC # type: _datetime.datetime # type: Union[str, _Timezone, None] -): # type: (...) -> DateTime + dt, tz=UTC +): # type: (_datetime.datetime, Optional[Union[str, _Timezone]]) -> DateTime """ Create a DateTime instance from a datetime one. """ @@ -198,7 +198,7 @@ def instance( ) -def now(tz=None): # type: (Union[str, _Timezone, None]) -> DateTime +def now(tz=None): # type: (Optional[Union[str, _Timezone]]) -> DateTime """ Get a DateTime instance for the current date and time. """ @@ -248,7 +248,7 @@ def from_format( string, # type: str fmt, # type: str tz=UTC, # type: Union[str, _Timezone] - locale=None, # type: Union[str, None] + locale=None, # type: Optional[str] ): # type: (...) -> DateTime """ Creates a DateTime instance from a specific format. @@ -261,8 +261,8 @@ def from_format( def from_timestamp( - timestamp, tz=UTC # type: Union[int, float] # type: Union[str, _Timezone] -): # type: (...) -> DateTime + timestamp, tz=UTC +): # type: (Union[int, float], Union[str, _Timezone]) -> DateTime """ Create a DateTime instance from a timestamp. """ @@ -305,9 +305,7 @@ def duration( ) -def period( - start, end, absolute=False # type: DateTime # type: DateTime # type: bool -): # type: (...) -> Period +def period(start, end, absolute=False): # type: (DateTime, DateTime, bool) -> Period """ Create a Period instance. """ diff --git a/pendulum/_extensions/helpers.py b/pendulum/_extensions/helpers.py index 7bcf31ec..1578f97e 100644 --- a/pendulum/_extensions/helpers.py +++ b/pendulum/_extensions/helpers.py @@ -2,6 +2,7 @@ from collections import namedtuple import datetime +import typing from ..constants import ( EPOCH_YEAR, @@ -48,18 +49,18 @@ def __repr__(self): ) -def is_leap(year): +def is_leap(year): # type: (int) -> bool return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0) -def is_long_year(year): +def is_long_year(year): # type: (int) -> bool def p(y): return y + y // 4 - y // 100 + y // 400 return p(year) % 7 == 4 or p(year - 1) % 7 == 3 -def week_day(year, month, day): +def week_day(year, month, day): # type: (int, int, int) -> int if month < 3: year -= 1 @@ -78,14 +79,14 @@ def week_day(year, month, day): return w -def days_in_year(year): +def days_in_year(year): # type: (int) -> int if is_leap(year): return DAYS_PER_L_YEAR return DAYS_PER_N_YEAR -def timestamp(dt): # type: (datetime) -> int +def timestamp(dt): # type: (datetime.datetime) -> int year = dt.year result = (year - 1970) * 365 + MONTHS_OFFSETS[0][dt.month] @@ -107,7 +108,9 @@ def timestamp(dt): # type: (datetime) -> int return result -def local_time(unix_time, utc_offset, microseconds): +def local_time( + unix_time, utc_offset, microseconds +): # type: (int, int, int) -> typing.Tuple[int, int, int, int, int, int, int] """ Returns a UNIX time as a broken down time for a particular transition type. @@ -182,7 +185,9 @@ def local_time(unix_time, utc_offset, microseconds): return (year, month, day, hour, minute, second, microseconds) -def precise_diff(d1, d2): +def precise_diff( + d1, d2 +): # type: (typing.Union[datetime.datetime, datetime.date], typing.Union[datetime.datetime, datetime.date]) -> PreciseDiff """ Calculate a precise difference between two datetimes. @@ -341,7 +346,7 @@ def precise_diff(d1, d2): ) -def _day_number(year, month, day): +def _day_number(year, month, day): # type: (int, int, int) -> int month = (month + 9) % 12 year = year - month // 10 diff --git a/pendulum/datetime.py b/pendulum/datetime.py index abca24a8..945a3ec1 100644 --- a/pendulum/datetime.py +++ b/pendulum/datetime.py @@ -6,7 +6,7 @@ import datetime import pendulum -from typing import Union +from typing import Union, Optional, TypeVar from .date import Date from .time import Time @@ -37,9 +37,13 @@ W3C, ) +_D = TypeVar("_D", bound="DateTime") + class DateTime(datetime.datetime, Date): + EPOCH = None # type: DateTime + # Formats _FORMATS = { @@ -93,7 +97,7 @@ def __new__( return self @classmethod - def now(cls, tz=None): # type: (Union[str, Timezone, None]) -> DateTime + def now(cls, tz=None): # type: (Optional[Union[str, Timezone]]) -> DateTime """ Get a DateTime instance for the current date and time. """ @@ -214,21 +218,21 @@ def offset_hours(self): return self.get_offset() / SECONDS_PER_MINUTE / MINUTES_PER_HOUR @property - def timezone(self): # type: () -> Union[str, None] + def timezone(self): # type: () -> Optional[Timezone] if not isinstance(self.tzinfo, Timezone): return return self.tzinfo @property - def tz(self): # type: () -> Union[str, None] + def tz(self): # type: () -> Optional[Timezone] return self.timezone @property - def timezone_name(self): # type: () -> Union[str, None] + def timezone_name(self): # type: () -> Optional[str] tz = self.timezone - if self.timezone is None: + if tz is None: return None return tz.name @@ -255,7 +259,7 @@ def date(self): def time(self): return Time(self.hour, self.minute, self.second, self.microsecond) - def naive(self): # type: () -> DateTime + def naive(self): # type: (_D) -> _D """ Return the DateTime without timezone information. """ @@ -597,7 +601,7 @@ def add( minutes=0, seconds=0, microseconds=0, - ): # type: (int, int, int, int, int, int, int) -> DateTime + ): # type: (_D, int, int, int, int, int, int, int, int) -> _D """ Add a duration to the instance. @@ -794,10 +798,10 @@ def diff(self, dt=None, abs=True): def diff_for_humans( self, - other=None, # type: Union['DateTime', None] + other=None, # type: Optional[DateTime] absolute=False, # type: bool - locale=None, # type:Union[str, None] - ): # type: (...) -> False + locale=None, # type: Optional[str] + ): # type: (...) -> str """ Get the difference in a human readable format in the current locale. diff --git a/pendulum/formatting/difference_formatter.py b/pendulum/formatting/difference_formatter.py index 58d3876b..7ab6a73d 100644 --- a/pendulum/formatting/difference_formatter.py +++ b/pendulum/formatting/difference_formatter.py @@ -1,6 +1,9 @@ from pendulum.utils._compat import decode from ..locales.locale import Locale +from ..period import Period + +import typing class DifferenceFormatter(object): @@ -11,7 +14,9 @@ class DifferenceFormatter(object): def __init__(self, locale="en"): self._locale = Locale.load(locale) - def format(self, diff, is_now=True, absolute=False, locale=None): + def format( + self, diff, is_now=True, absolute=False, locale=None + ): # type: (Period, bool, bool, typing.Optional[str]) -> str """ Formats a difference. diff --git a/pendulum/formatting/formatter.py b/pendulum/formatting/formatter.py index b41873ca..d400c287 100644 --- a/pendulum/formatting/formatter.py +++ b/pendulum/formatting/formatter.py @@ -229,7 +229,9 @@ class Formatter: "z": str, } - def format(self, dt, fmt, locale=None): + def format( + self, dt, fmt, locale=None + ): # type: (pendulum.DateTime, str, typing.Optional[typing.Union[str, Locale]]) -> str """ Formats a DateTime instance with a given format and locale. @@ -260,7 +262,9 @@ def format(self, dt, fmt, locale=None): return decode(result) - def _format_token(self, dt, token, locale): + def _format_token( + self, dt, token, locale + ): # type: (pendulum.DateTime, str, Locale) -> str """ Formats a DateTime instance with a given token and locale. @@ -306,7 +310,9 @@ def _format_token(self, dt, token, locale): return "{}{:02d}{}{:02d}".format(sign, hour, separator, minute) - def _format_localizable_token(self, dt, token, locale): + def _format_localizable_token( + self, dt, token, locale + ): # type: (pendulum.DateTime, str, Locale) -> str """ Formats a DateTime instance with a given localizable token and locale. @@ -360,8 +366,8 @@ def parse( time, # type: str fmt, # type: str now, # type: pendulum.DateTime - locale=None, # type: typing.Union[str, None] - ): # type: (...) -> dict + locale=None, # type: typing.Optional[str] + ): # type: (...) -> typing.Dict[str, typing.Any] """ Parses a time string matching a given format as a tuple. @@ -410,7 +416,9 @@ def parse( return self._check_parsed(parsed, now) - def _check_parsed(self, parsed, now): # type: (dict, pendulum.DateTime) -> dict + def _check_parsed( + self, parsed, now + ): # type: (typing.Dict[str, typing.Any], pendulum.DateTime) -> typing.Dict[str, typing.Any] """ Checks validity of parsed elements. @@ -530,7 +538,7 @@ def _check_parsed(self, parsed, now): # type: (dict, pendulum.DateTime) -> dict def _get_parsed_values( self, m, parsed, locale, now - ): # type: (..., dict, Locale, pendulum.DateTime) -> None + ): # type: (typing.Match[str], typing.Dict[str, typing.Any], Locale, pendulum.DateTime) -> None for token, index in m.re.groupindex.items(): if token in self._LOCALIZABLE_TOKENS: self._get_parsed_locale_value(token, m.group(index), parsed, locale) @@ -539,7 +547,7 @@ def _get_parsed_values( def _get_parsed_value( self, token, value, parsed, now - ): # type: (str, str, dict, pendulum.DateTime) -> None + ): # type: (str, str, typing.Dict[str, typing.Any], pendulum.DateTime) -> None parsed_token = self._PARSE_TOKENS[token](value) if "Y" in token: @@ -599,7 +607,7 @@ def _get_parsed_value( def _get_parsed_locale_value( self, token, value, parsed, locale - ): # type: (str, str, dict, Locale) -> None + ): # type: (str, str, typing.Dict[str, typing.Any], Locale) -> None if token == "MMMM": unit = "month" match = "months.wide" diff --git a/pendulum/helpers.py b/pendulum/helpers.py index 5085a38b..dc87ce57 100644 --- a/pendulum/helpers.py +++ b/pendulum/helpers.py @@ -7,10 +7,17 @@ from math import copysign from datetime import datetime, date, timedelta from contextlib import contextmanager -from typing import Union +from typing import TYPE_CHECKING, Union, Optional, TypeVar, Iterator, overload + +if TYPE_CHECKING: + # Prevent import cycles + from .period import Period with_extensions = os.getenv("PENDULUM_EXTENSIONS", "1") == "1" +_DT = TypeVar("_DT", bound=datetime) +_D = TypeVar("_D", bound=date) + try: if not with_extensions or struct.calcsize("P") == 4: raise ImportError() @@ -43,8 +50,9 @@ difference_formatter = DifferenceFormatter() +@overload def add_duration( - dt, # type: Union[datetime, date] + dt, # type: _DT years=0, # type: int months=0, # type: int weeks=0, # type: int @@ -52,8 +60,33 @@ def add_duration( hours=0, # type: int minutes=0, # type: int seconds=0, # type: int + microseconds=0, # type: int +): # type: (...) -> _DT + ... + + +@overload +def add_duration( + dt, # type: _D + years=0, # type: int + months=0, # type: int + weeks=0, # type: int + days=0, # type: int +): # type: (...) -> _D + ... + + +def add_duration( + dt, + years=0, + months=0, + weeks=0, + days=0, + hours=0, + minutes=0, + seconds=0, microseconds=0, -): # type: (...) -> Union[datetime, date] +): """ Adds a duration to a date/datetime instance. """ @@ -122,7 +155,9 @@ def add_duration( ) -def format_diff(diff, is_now=True, absolute=False, locale=None): +def format_diff( + diff, is_now=True, absolute=False, locale=None +): # type: (Period, bool, bool, Optional[str]) -> str if locale is None: locale = get_locale() @@ -137,7 +172,7 @@ def _sign(x): @contextmanager -def test(mock): +def test(mock): # type: (pendulum.DateTime) -> Iterator[None] set_test_now(mock) yield @@ -145,11 +180,11 @@ def test(mock): set_test_now() -def set_test_now(test_now=None): +def set_test_now(test_now=None): # type: (Optional[pendulum.DateTime]) -> None pendulum._TEST_NOW = test_now -def get_test_now(): # type: () -> pendulum.DateTime +def get_test_now(): # type: () -> Optional[pendulum.DateTime] return pendulum._TEST_NOW @@ -157,28 +192,28 @@ def has_test_now(): # type: () -> bool return pendulum._TEST_NOW is not None -def locale(name): +def locale(name): # type: (str) -> Locale return Locale.load(name) -def set_locale(name): +def set_locale(name): # type: (str) -> None locale(name) pendulum._LOCALE = name -def get_locale(): +def get_locale(): # type: () -> str return pendulum._LOCALE -def week_starts_at(wday): +def week_starts_at(wday): # type: (int) -> None if wday < pendulum.SUNDAY or wday > pendulum.SATURDAY: raise ValueError("Invalid week day as start of week.") pendulum._WEEK_STARTS_AT = wday -def week_ends_at(wday): +def week_ends_at(wday): # type: (int) -> None if wday < pendulum.SUNDAY or wday > pendulum.SATURDAY: raise ValueError("Invalid week day as start of week.") diff --git a/pendulum/locales/locale.py b/pendulum/locales/locale.py index 84429112..2ce0694f 100644 --- a/pendulum/locales/locale.py +++ b/pendulum/locales/locale.py @@ -3,6 +3,7 @@ import os import re +from typing import Union, Optional, Any from importlib import import_module from pendulum.utils._compat import basestring @@ -16,13 +17,13 @@ class Locale: _cache = {} - def __init__(self, locale, data): + def __init__(self, locale, data): # type: (str, Any) -> None self._locale = locale self._data = data self._key_cache = {} @classmethod - def load(cls, locale): + def load(cls, locale): # type: (Union[str, Locale]) -> Locale if isinstance(locale, Locale): return locale @@ -46,14 +47,14 @@ def load(cls, locale): return cls._cache[locale] @classmethod - def normalize_locale(cls, locale): + def normalize_locale(cls, locale): # type: (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): + def get(self, key, default=None): # type: (str, Optional[Any]) -> Any if key in self._key_cache: return self._key_cache[key] @@ -72,16 +73,16 @@ def get(self, key, default=None): return self._key_cache[key] - def translation(self, key): + def translation(self, key): # type: (str) -> Any return self.get("translations.{}".format(key)) - def plural(self, number): + def plural(self, number): # type: (int) -> str return decode(self._data["plural"](number)) - def ordinal(self, number): + def ordinal(self, number): # type: (int) -> str return decode(self._data["ordinal"](number)) - def ordinalize(self, number): + def ordinalize(self, number): # type: (int) -> str ordinal = self.get("custom.ordinal.{}".format(self.ordinal(number))) if not ordinal: diff --git a/pendulum/parser.py b/pendulum/parser.py index a19907cf..624ee577 100644 --- a/pendulum/parser.py +++ b/pendulum/parser.py @@ -2,6 +2,7 @@ import pendulum import datetime +import typing from .parsing import parse as base_parse, _Interval @@ -13,7 +14,7 @@ from .tz import UTC -def parse(text, **options): +def parse(text, **options): # type: (str, **typing.Any) -> str # Use the mock now value if it exists options["now"] = options.get("now", pendulum.get_test_now()) diff --git a/pendulum/py.typed b/pendulum/py.typed new file mode 100644 index 00000000..e69de29b diff --git a/pendulum/tz/__init__.py b/pendulum/tz/__init__.py index 74ba7820..cf09e135 100644 --- a/pendulum/tz/__init__.py +++ b/pendulum/tz/__init__.py @@ -1,6 +1,6 @@ import pytzdata -from typing import Union +from typing import Union, Tuple from .local_timezone import get_local_timezone from .local_timezone import set_local_timezone @@ -14,13 +14,13 @@ POST_TRANSITION = "post" TRANSITION_ERROR = "error" -timezones = pytzdata.timezones +timezones = pytzdata.timezones # type: Tuple[str, ...] _tz_cache = {} -def timezone(name, extended=True): # type: (Union[str, int]) -> _Timezone +def timezone(name, extended=True): # type: (Union[str, int], bool) -> _Timezone """ Return a Timezone instance given its name. """ @@ -44,7 +44,7 @@ def fixed_timezone(offset): # type: (int) -> _FixedTimezone Return a Timezone instance given its offset in seconds. """ if offset in _tz_cache: - return _tz_cache[offset] + return _tz_cache[offset] # type: ignore tz = _FixedTimezone(offset) _tz_cache[offset] = tz diff --git a/pendulum/tz/local_timezone.py b/pendulum/tz/local_timezone.py index ca094f77..1bbbf38c 100644 --- a/pendulum/tz/local_timezone.py +++ b/pendulum/tz/local_timezone.py @@ -11,7 +11,7 @@ winreg = None from contextlib import contextmanager -from typing import Union +from typing import Optional, Union, Iterator from .timezone import Timezone, TimezoneFile from .zoneinfo.exceptions import InvalidTimezone @@ -35,14 +35,14 @@ def get_local_timezone(): # type: () -> Timezone return _local_timezone -def set_local_timezone(mock=None): # type: (Union[str, Timezone, None]) -> None +def set_local_timezone(mock=None): # type: (Optional[Union[str, Timezone]]) -> None global _mock_local_timezone _mock_local_timezone = mock @contextmanager -def test_local_timezone(mock): # type: (Timezone) -> None +def test_local_timezone(mock): # type: (Timezone) -> Iterator[None] set_local_timezone(mock) yield diff --git a/pendulum/tz/timezone.py b/pendulum/tz/timezone.py index a79242ed..e3f1406e 100644 --- a/pendulum/tz/timezone.py +++ b/pendulum/tz/timezone.py @@ -1,7 +1,7 @@ import pendulum from datetime import datetime, timedelta, tzinfo -from typing import Optional, Union +from typing import Optional, TypeVar, overload from pendulum.helpers import local_time, timestamp from pendulum.utils._compat import _HAS_FOLD @@ -15,6 +15,9 @@ PRE_TRANSITION = "pre" TRANSITION_ERROR = "error" +_datetime = datetime +_D = TypeVar("_D", bound=datetime) + class Timezone(tzinfo): """ @@ -26,7 +29,7 @@ class Timezone(tzinfo): >>> tz = Timezone('Europe/Paris') """ - def __init__(self, name, extended=True): # type: (str) -> None + def __init__(self, name, extended=True): # type: (str, bool) -> None tz = read(name, extend=extended) self._name = name @@ -37,9 +40,7 @@ def __init__(self, name, extended=True): # type: (str) -> None def name(self): # type: () -> str return self._name - def convert( - self, dt, dst_rule=None # type: datetime # type: Union[str, None] - ): # type: (...) -> datetime + def convert(self, dt, dst_rule=None): # type: (_D, Optional[str]) -> _D """ Converts a datetime in the current timezone. @@ -67,7 +68,7 @@ def convert( def datetime( self, year, month, day, hour=0, minute=0, second=0, microsecond=0 - ): # type: (int, int, int, int, int, int, int) -> datetime + ): # type: (int, int, int, int, int, int, int) -> _datetime """ Return a normalized datetime for the current timezone. """ @@ -81,9 +82,7 @@ def datetime( dst_rule=POST_TRANSITION, ) - def _normalize( - self, dt, dst_rule=None # type: datetime # type: Union[str, None] - ): # type: (...) -> datetime + def _normalize(self, dt, dst_rule=None): # type: (_D, Optional[str]) -> _D sec = timestamp(dt) fold = 0 transition = self._lookup_transition(sec) @@ -134,7 +133,7 @@ def _normalize( return dt.__class__(*local_time(sec, 0, dt.microsecond), **kwargs) - def _convert(self, dt): # type: (datetime) -> datetime + def _convert(self, dt): # type: (_D) -> _D if dt.tzinfo is self: return self._normalize(dt, dst_rule=POST_TRANSITION) @@ -176,8 +175,8 @@ def _convert(self, dt): # type: (datetime) -> datetime return dt.__class__(*local_time(stamp, 0, dt.microsecond), **kwargs) def _lookup_transition( - self, stamp, is_utc=False # type: int # type: bool - ): # type: (...) -> Transition + self, stamp, is_utc=False + ): # type: (int, bool) -> Transition lo, hi = 0, len(self._transitions) hint = self._hint[is_utc] if hint: @@ -211,9 +210,15 @@ def _lookup_transition( return self._transitions[lo] - def utcoffset( - self, dt # type: Optional[datetime] - ): # type: (...) -> Union[timedelta, None] + @overload + def utcoffset(self, dt): # type: (None) -> None + ... + + @overload + def utcoffset(self, dt): # type: (_datetime) -> timedelta + ... + + def utcoffset(self, dt): if dt is None: return @@ -222,8 +227,8 @@ def utcoffset( return transition.utcoffset() def dst( - self, dt # type: Optional[datetime] - ): # type: (...) -> Union[timedelta, None] + self, dt # type: Optional[_datetime] + ): # type: (...) -> Optional[timedelta] if dt is None: return @@ -234,7 +239,7 @@ def dst( return timedelta(seconds=transition.fix) - def tzname(self, dt): # type: Optional[datetime] # type: (...) -> Union[str, None] + def tzname(self, dt): # type: (Optional[_datetime]) -> Optional[str] if dt is None: return @@ -242,7 +247,7 @@ def tzname(self, dt): # type: Optional[datetime] # type: (...) -> Union[str, N return transition.ttype.abbreviation - def _get_transition(self, dt): # type: (datetime) -> Transition + def _get_transition(self, dt): # type: (_datetime) -> Transition if dt.tzinfo is not None and dt.tzinfo is not self: dt = dt - dt.utcoffset() @@ -266,7 +271,7 @@ def _get_transition(self, dt): # type: (datetime) -> Transition return transition - def fromutc(self, dt): # type: (datetime) -> datetime + def fromutc(self, dt): # type: (_D) -> _D stamp = timestamp(dt) transition = self._lookup_transition(stamp, is_utc=True) @@ -302,7 +307,7 @@ def __init__(self, offset, name=None): def offset(self): # type: () -> int return self._offset - def _normalize(self, dt, **_): # type: (datetime, ...) -> datetime + def _normalize(self, dt, dst_rule=None): # type: (_D, Optional[str]) -> _D if _HAS_FOLD: dt = dt.__class__( dt.year, @@ -329,22 +334,22 @@ def _normalize(self, dt, **_): # type: (datetime, ...) -> datetime return dt - def _convert(self, dt): # type: (datetime) -> datetime + def _convert(self, dt): # type: (_D) -> _D if dt.tzinfo is not self: return dt.astimezone(self) return dt - def utcoffset(self, dt): # type: Optional[datetime] # type: (...) -> timedelta + def utcoffset(self, dt): # type: (Optional[_datetime]) -> timedelta return self._utcoffset - def dst(self, dt): # type: Optional[datetime] # type: (...) -> timedelta + def dst(self, dt): # type: (Optional[_datetime]) -> timedelta return timedelta() - def fromutc(self, dt): # type: (datetime) -> datetime + def fromutc(self, dt): # type: (_D) -> _D return (dt + self._utcoffset).replace(tzinfo=self) - def tzname(self, dt): # type: Optional[datetime] # type: (...) -> Union[str, None] + def tzname(self, dt): # type: (Optional[_datetime]) -> Optional[str] return self._name def __getinitargs__(self): # type: () -> tuple diff --git a/pendulum/tz/zoneinfo/__init__.py b/pendulum/tz/zoneinfo/__init__.py index ce70e5fd..c1833650 100644 --- a/pendulum/tz/zoneinfo/__init__.py +++ b/pendulum/tz/zoneinfo/__init__.py @@ -2,14 +2,14 @@ from .timezone import Timezone -def read(name, extend=True): # type: (str) -> Timezone +def read(name, extend=True): # type: (str, bool) -> Timezone """ Read the zoneinfo structure for a given timezone name. """ return Reader(extend=extend).read_for(name) -def read_file(path, extend=True): # type: (str) -> Timezone +def read_file(path, extend=True): # type: (str, bool) -> Timezone """ Read the zoneinfo structure for a given path. """ diff --git a/pendulum/tz/zoneinfo/posix_timezone.py b/pendulum/tz/zoneinfo/posix_timezone.py index b5b0ceb2..da5e8667 100644 --- a/pendulum/tz/zoneinfo/posix_timezone.py +++ b/pendulum/tz/zoneinfo/posix_timezone.py @@ -4,7 +4,7 @@ """ import re -from typing import Union +from typing import Optional from pendulum.constants import MONTHS_OFFSETS, SECS_PER_DAY @@ -67,7 +67,7 @@ def _posix_spec(spec): # type: (str) -> PosixTimezone return PosixTimezone(std_abbr, std_offset, dst_abbr, dst_offset, dst_start, dst_end) -def _parse_abbr(text): # type: (str) -> Union[str, None] +def _parse_abbr(text): # type: (str) -> str return text.lstrip("<").rstrip(">") @@ -231,10 +231,10 @@ def __init__( self, std_abbr, # type: str std_offset, # type: int - dst_abbr, # type: Union[str, None] = None - dst_offset, # type: Union[str, None] = None - dst_start=None, # type: Union[PosixTransition, None] - dst_end=None, # type: Union[PosixTransition, None] + dst_abbr, # type: Optional[str] + dst_offset, # type: Optional[int] + dst_start=None, # type: Optional[PosixTransition] + dst_end=None, # type: Optional[PosixTransition] ): self._std_abbr = std_abbr self._std_offset = std_offset @@ -252,17 +252,17 @@ def std_offset(self): # type: () -> int return self._std_offset @property - def dst_abbr(self): # type: () -> Union[str, None] + def dst_abbr(self): # type: () -> Optional[str] return self._dst_abbr @property - def dst_offset(self): # type: () -> Union[int, None] + def dst_offset(self): # type: () -> Optional[int] return self._dst_offset @property - def dst_start(self): # type: () -> Union[PosixTransition, None] + def dst_start(self): # type: () -> Optional[PosixTransition] return self._dst_start @property - def dst_end(self): # type: () -> Union[PosixTransition, None] + def dst_end(self): # type: () -> Optional[PosixTransition] return self._dst_end diff --git a/pendulum/tz/zoneinfo/reader.py b/pendulum/tz/zoneinfo/reader.py index f494197d..21848b5c 100644 --- a/pendulum/tz/zoneinfo/reader.py +++ b/pendulum/tz/zoneinfo/reader.py @@ -3,7 +3,7 @@ from collections import namedtuple from struct import unpack -from typing import Dict, List, Optional +from typing import Dict, List, Optional, IO, Any, Tuple from pytzdata.exceptions import TimezoneNotFound @@ -155,7 +155,7 @@ def _parse_header(self, fd): # type: (...) -> header return hdr - def _parse_trans_64(self, fd, n): # type: (..., int) -> List[int] + def _parse_trans_64(self, fd, n): # type: (IO[Any], int) -> List[int] trans = [] for _ in range(n): buff = self._check_read(fd, 8) @@ -163,7 +163,7 @@ def _parse_trans_64(self, fd, n): # type: (..., int) -> List[int] return trans - def _parse_trans_32(self, fd, n): # type: (..., int) -> List[int] + def _parse_trans_32(self, fd, n): # type: (IO[Any], int) -> List[int] trans = [] for _ in range(n): buff = self._check_read(fd, 4) @@ -171,12 +171,14 @@ def _parse_trans_32(self, fd, n): # type: (..., int) -> List[int] return trans - def _parse_type_idx(self, fd, n): # type: (..., int) -> List[int] + def _parse_type_idx(self, fd, n): # type: (IO[Any], int) -> List[int] buff = self._check_read(fd, n) return list(unpack("{}B".format(n), buff)) - def _parse_types(self, fd, n): # type: (..., int) -> List[tuple] + def _parse_types( + self, fd, n + ): # type: (IO[Any], int) -> List[Tuple[Any, bool, int]] types = [] for _ in range(n): @@ -188,8 +190,8 @@ def _parse_types(self, fd, n): # type: (..., int) -> List[tuple] return types def _parse_abbrs( - self, fd, n, types # type: int # type: List[tuple] - ): # type: (...) -> Dict[int, str] + self, fd, n, types + ): # type: (IO[Any], int, List[Tuple[Any, bool, int]]) -> Dict[int, str] abbrs = {} buff = self._check_read(fd, n) diff --git a/pendulum/tz/zoneinfo/timezone.py b/pendulum/tz/zoneinfo/timezone.py index 4c19970a..cf8b126e 100644 --- a/pendulum/tz/zoneinfo/timezone.py +++ b/pendulum/tz/zoneinfo/timezone.py @@ -1,5 +1,5 @@ from datetime import datetime -from typing import List, Union +from typing import List, Optional from pendulum.constants import DAYS_PER_YEAR, SECS_PER_YEAR from pendulum.helpers import local_time, is_leap, timestamp, week_day @@ -13,7 +13,7 @@ class Timezone: def __init__( self, transitions, # type: List[Transition] - posix_rule=None, # type: Union[PosixTimezone, None] + posix_rule=None, # type: Optional[PosixTimezone] extended=True, # type: bool ): self._posix_rule = posix_rule diff --git a/pendulum/tz/zoneinfo/transition.py b/pendulum/tz/zoneinfo/transition.py index 1ba93a46..c64bcf2f 100644 --- a/pendulum/tz/zoneinfo/transition.py +++ b/pendulum/tz/zoneinfo/transition.py @@ -1,5 +1,5 @@ from datetime import timedelta -from typing import Union +from typing import Optional from .transition_type import TransitionType @@ -9,7 +9,7 @@ def __init__( self, at, # type: int ttype, # type: TransitionType - previous, # type: Union['Transition', None] + previous, # type: Optional[Transition] ): self._at = at @@ -51,7 +51,7 @@ def ttype(self): # type: () -> TransitionType return self._ttype @property - def previous(self): # type: () -> Transition + def previous(self): # type: () -> Optional[Transition] return self._previous @property @@ -67,7 +67,7 @@ def is_missing(self, stamp): # type: (int) -> bool def utcoffset(self): # type: () -> timedelta return self._utcoffset - def __contains__(self, stamp): # type: () -> bool + def __contains__(self, stamp): # type: (int) -> bool return self.previous.local <= stamp < self.local def __repr__(self): # type: () -> str diff --git a/pyproject.toml b/pyproject.toml index 34993096..944bbfd0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,6 +16,7 @@ packages = [ {include = "pendulum"}, {include = "tests", format = "sdist"}, ] +include = ["pendulum/py.typed"] [tool.poetry.dependencies] From c42734f2ab4b91ad9211ac7ce54c88cbc409f5ff Mon Sep 17 00:00:00 2001 From: Bryan Forbes Date: Fri, 30 Nov 2018 11:53:59 -0600 Subject: [PATCH 045/256] Change "..." to "pass" for Python 2 and pypy --- pendulum/helpers.py | 4 ++-- pendulum/tz/timezone.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pendulum/helpers.py b/pendulum/helpers.py index dc87ce57..fa8abed1 100644 --- a/pendulum/helpers.py +++ b/pendulum/helpers.py @@ -62,7 +62,7 @@ def add_duration( seconds=0, # type: int microseconds=0, # type: int ): # type: (...) -> _DT - ... + pass @overload @@ -73,7 +73,7 @@ def add_duration( weeks=0, # type: int days=0, # type: int ): # type: (...) -> _D - ... + pass def add_duration( diff --git a/pendulum/tz/timezone.py b/pendulum/tz/timezone.py index e3f1406e..c22a9888 100644 --- a/pendulum/tz/timezone.py +++ b/pendulum/tz/timezone.py @@ -212,11 +212,11 @@ def _lookup_transition( @overload def utcoffset(self, dt): # type: (None) -> None - ... + pass @overload def utcoffset(self, dt): # type: (_datetime) -> timedelta - ... + pass def utcoffset(self, dt): if dt is None: From 61c2ff60cffea31c796cce8e9494a2cf3018c877 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A5l=20Nilsen?= Date: Fri, 14 Sep 2018 14:06:59 +0200 Subject: [PATCH 046/256] =?UTF-8?q?Add=20Norwegian=20bokm=C3=A5l=20(nb)=20?= =?UTF-8?q?locale?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pendulum/locales/nb/__init__.py | 0 pendulum/locales/nb/custom.py | 23 +++++ pendulum/locales/nb/locale.py | 167 ++++++++++++++++++++++++++++++++ tests/localization/test_nb.py | 89 +++++++++++++++++ 4 files changed, 279 insertions(+) create mode 100644 pendulum/locales/nb/__init__.py create mode 100644 pendulum/locales/nb/custom.py create mode 100644 pendulum/locales/nb/locale.py create mode 100644 tests/localization/test_nb.py diff --git a/pendulum/locales/nb/__init__.py b/pendulum/locales/nb/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pendulum/locales/nb/custom.py b/pendulum/locales/nb/custom.py new file mode 100644 index 00000000..7f588ef2 --- /dev/null +++ b/pendulum/locales/nb/custom.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +""" +nn custom locale file. +""" + +translations = { + # Relative time + "after": "{0} etter", + "before": "{0} før", + # Ordinals + "ordinal": {"one": ".", "two": ".", "few": ".", "other": "."}, + # Date formats + "date_formats": { + "LTS": "HH:mm:ss", + "LT": "HH:mm", + "LLLL": "dddd Do MMMM YYYY HH:mm", + "LLL": "Do MMMM YYYY HH:mm", + "LL": "Do MMMM YYYY", + "L": "DD.MM.YYYY", + }, +} diff --git a/pendulum/locales/nb/locale.py b/pendulum/locales/nb/locale.py new file mode 100644 index 00000000..732b875d --- /dev/null +++ b/pendulum/locales/nb/locale.py @@ -0,0 +1,167 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +""" +nb locale file. + +It has been generated automatically and must not be modified directly. +""" + +from .custom import translations as custom_translations + + +locale = { + 'plural': lambda n: 'one' if (n == n and ((n == 1))) else 'other', + 'ordinal': lambda n: 'other', + 'translations': { + 'days': { + 'abbreviated': { + 0: 'søn.', + 1: 'man.', + 2: 'tir.', + 3: 'ons.', + 4: 'tor.', + 5: 'fre.', + 6: 'lør.', + }, + 'narrow': {0: 'S', 1: 'M', 2: 'T', 3: 'O', 4: 'T', 5: 'F', 6: 'L'}, + 'short': { + 0: 'sø.', + 1: 'ma.', + 2: 'ti.', + 3: 'on.', + 4: 'to.', + 5: 'fr.', + 6: 'lø.', + }, + 'wide': { + 0: 'søndag', + 1: 'mandag', + 2: 'tirsdag', + 3: 'onsdag', + 4: 'torsdag', + 5: 'fredag', + 6: 'lørdag', + }, + }, + 'months': { + 'abbreviated': { + 1: 'jan.', + 2: 'feb.', + 3: 'mar.', + 4: 'apr.', + 5: 'mai', + 6: 'jun.', + 7: 'jul.', + 8: 'aug.', + 9: 'sep.', + 10: 'okt.', + 11: 'nov.', + 12: 'des.', + }, + 'narrow': { + 1: 'J', + 2: 'F', + 3: 'M', + 4: 'A', + 5: 'M', + 6: 'J', + 7: 'J', + 8: 'A', + 9: 'S', + 10: 'O', + 11: 'N', + 12: 'D', + }, + 'wide': { + 1: 'januar', + 2: 'februar', + 3: 'mars', + 4: 'april', + 5: 'mai', + 6: 'juni', + 7: 'juli', + 8: 'august', + 9: 'september', + 10: 'oktober', + 11: 'november', + 12: 'desember', + }, + }, + 'units': { + 'year': {'one': '{0} år', 'other': '{0} år'}, + 'month': {'one': '{0} måned', 'other': '{0} måneder'}, + 'week': {'one': '{0} uke', 'other': '{0} uker'}, + 'day': {'one': '{0} dag', 'other': '{0} dager'}, + 'hour': {'one': '{0} time', 'other': '{0} timer'}, + 'minute': {'one': '{0} minutt', 'other': '{0} minutter'}, + 'second': {'one': '{0} sekund', 'other': '{0} sekunder'}, + 'microsecond': { + 'one': '{0} mikrosekund', + 'other': '{0} mikrosekunder', + }, + }, + 'relative': { + 'year': { + 'future': {'other': 'om {0} år', 'one': 'om {0} år'}, + 'past': { + 'other': 'for {0} år siden', + 'one': 'for {0} år siden', + }, + }, + 'month': { + 'future': {'other': 'om {0} måneder', 'one': 'om {0} måned'}, + 'past': { + 'other': 'for {0} måneder siden', + 'one': 'for {0} måned siden', + }, + }, + 'week': { + 'future': {'other': 'om {0} uker', 'one': 'om {0} uke'}, + 'past': { + 'other': 'for {0} uker siden', + 'one': 'for {0} uke siden', + }, + }, + 'day': { + 'future': {'other': 'om {0} dager', 'one': 'om {0} dag'}, + 'past': { + 'other': 'for {0} dager siden', + 'one': 'for {0} dag siden', + }, + }, + 'hour': { + 'future': {'other': 'om {0} timer', 'one': 'om {0} time'}, + 'past': { + 'other': 'for {0} timer siden', + 'one': 'for {0} time siden', + }, + }, + 'minute': { + 'future': {'other': 'om {0} minutter', 'one': 'om {0} minutt'}, + 'past': { + 'other': 'for {0} minutter siden', + 'one': 'for {0} minutt siden', + }, + }, + 'second': { + 'future': {'other': 'om {0} sekunder', 'one': 'om {0} sekund'}, + 'past': { + 'other': 'for {0} sekunder siden', + 'one': 'for {0} sekund siden', + }, + }, + }, + 'day_periods': { + 'midnight': 'midnatt', + 'am': 'a.m.', + 'pm': 'p.m.', + 'morning1': 'morgenen', + 'morning2': 'formiddagen', + 'afternoon1': 'ettermiddagen', + 'evening1': 'kvelden', + 'night1': 'natten', + }, + }, + 'custom': custom_translations, +} diff --git a/tests/localization/test_nb.py b/tests/localization/test_nb.py new file mode 100644 index 00000000..f9be7b58 --- /dev/null +++ b/tests/localization/test_nb.py @@ -0,0 +1,89 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +import pendulum + + +locale = "nb" + + +def test_diff_for_humans(): + with pendulum.test(pendulum.datetime(2016, 8, 29)): + diff_for_humans() + + +def diff_for_humans(): + d = pendulum.now().subtract(seconds=1) + assert d.diff_for_humans(locale=locale) == "for 1 sekund siden" + + d = pendulum.now().subtract(seconds=2) + assert d.diff_for_humans(locale=locale) == "for 2 sekunder siden" + + d = pendulum.now().subtract(minutes=1) + assert d.diff_for_humans(locale=locale) == "for 1 minutt siden" + + d = pendulum.now().subtract(minutes=2) + assert d.diff_for_humans(locale=locale) == "for 2 minutter siden" + + d = pendulum.now().subtract(hours=1) + assert d.diff_for_humans(locale=locale) == "for 1 time siden" + + d = pendulum.now().subtract(hours=2) + assert d.diff_for_humans(locale=locale) == "for 2 timer siden" + + d = pendulum.now().subtract(days=1) + assert d.diff_for_humans(locale=locale) == "for 1 dag siden" + + d = pendulum.now().subtract(days=2) + assert d.diff_for_humans(locale=locale) == "for 2 dager siden" + + d = pendulum.now().subtract(weeks=1) + assert d.diff_for_humans(locale=locale) == "for 1 uke siden" + + d = pendulum.now().subtract(weeks=2) + assert d.diff_for_humans(locale=locale) == "for 2 uker siden" + + d = pendulum.now().subtract(months=1) + assert d.diff_for_humans(locale=locale) == "for 1 måned siden" + + d = pendulum.now().subtract(months=2) + assert d.diff_for_humans(locale=locale) == "for 2 måneder siden" + + d = pendulum.now().subtract(years=1) + assert d.diff_for_humans(locale=locale) == "for 1 år siden" + + d = pendulum.now().subtract(years=2) + assert d.diff_for_humans(locale=locale) == "for 2 år siden" + + d = pendulum.now().add(seconds=1) + assert d.diff_for_humans(locale=locale) == "om 1 sekund" + + d = pendulum.now().add(seconds=1) + d2 = pendulum.now() + assert d.diff_for_humans(d2, locale=locale) == "1 sekund etter" + assert d2.diff_for_humans(d, locale=locale) == "1 sekund før" + + assert d.diff_for_humans(d2, True, locale=locale) == "1 sekund" + assert ( + d2.diff_for_humans(d.add(seconds=1), True, locale=locale) + == "2 sekunder" + ) + + +def test_format(): + d = pendulum.datetime(2016, 8, 28, 7, 3, 6, 123456) + assert d.format("dddd", locale=locale) == "søndag" + assert d.format("ddd", locale=locale) == "søn." + assert d.format("MMMM", locale=locale) == "august" + assert d.format("MMM", locale=locale) == "aug." + assert d.format("A", locale=locale) == "a.m." + assert d.format("Qo", locale=locale) == "3." + assert d.format("Mo", locale=locale) == "8." + assert d.format("Do", locale=locale) == "28." + + assert d.format("LT", locale=locale) == "07:03" + assert d.format("LTS", locale=locale) == "07:03:06" + assert d.format("L", locale=locale) == "28.08.2016" + assert d.format("LL", locale=locale) == "28. august 2016" + assert d.format("LLL", locale=locale) == "28. august 2016 07:03" + assert d.format("LLLL", locale=locale) == "søndag 28. august 2016 07:03" From ac7bc9322c10558ece6f24b97f2c286b236b589b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A5l=20Nilsen?= Date: Fri, 14 Sep 2018 14:07:17 +0200 Subject: [PATCH 047/256] Add Norwegian nynorsk (nn) locale --- pendulum/locales/nn/__init__.py | 0 pendulum/locales/nn/custom.py | 23 +++++ pendulum/locales/nn/locale.py | 158 ++++++++++++++++++++++++++++++++ tests/localization/test_nn.py | 88 ++++++++++++++++++ 4 files changed, 269 insertions(+) create mode 100644 pendulum/locales/nn/__init__.py create mode 100644 pendulum/locales/nn/custom.py create mode 100644 pendulum/locales/nn/locale.py create mode 100644 tests/localization/test_nn.py diff --git a/pendulum/locales/nn/__init__.py b/pendulum/locales/nn/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pendulum/locales/nn/custom.py b/pendulum/locales/nn/custom.py new file mode 100644 index 00000000..7f588ef2 --- /dev/null +++ b/pendulum/locales/nn/custom.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +""" +nn custom locale file. +""" + +translations = { + # Relative time + "after": "{0} etter", + "before": "{0} før", + # Ordinals + "ordinal": {"one": ".", "two": ".", "few": ".", "other": "."}, + # Date formats + "date_formats": { + "LTS": "HH:mm:ss", + "LT": "HH:mm", + "LLLL": "dddd Do MMMM YYYY HH:mm", + "LLL": "Do MMMM YYYY HH:mm", + "LL": "Do MMMM YYYY", + "L": "DD.MM.YYYY", + }, +} diff --git a/pendulum/locales/nn/locale.py b/pendulum/locales/nn/locale.py new file mode 100644 index 00000000..6bea19b7 --- /dev/null +++ b/pendulum/locales/nn/locale.py @@ -0,0 +1,158 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +""" +nn locale file. + +It has been generated automatically and must not be modified directly. +""" + +from .custom import translations as custom_translations + + +locale = { + 'plural': lambda n: 'one' if (n == n and ((n == 1))) else 'other', + 'ordinal': lambda n: 'other', + 'translations': { + 'days': { + 'abbreviated': { + 0: 'søn.', + 1: 'mån.', + 2: 'tys.', + 3: 'ons.', + 4: 'tor.', + 5: 'fre.', + 6: 'lau.', + }, + 'narrow': {0: 'S', 1: 'M', 2: 'T', 3: 'O', 4: 'T', 5: 'F', 6: 'L'}, + 'short': { + 0: 'sø.', + 1: 'må.', + 2: 'ty.', + 3: 'on.', + 4: 'to.', + 5: 'fr.', + 6: 'la.', + }, + 'wide': { + 0: 'søndag', + 1: 'måndag', + 2: 'tysdag', + 3: 'onsdag', + 4: 'torsdag', + 5: 'fredag', + 6: 'laurdag', + }, + }, + 'months': { + 'abbreviated': { + 1: 'jan.', + 2: 'feb.', + 3: 'mars', + 4: 'apr.', + 5: 'mai', + 6: 'juni', + 7: 'juli', + 8: 'aug.', + 9: 'sep.', + 10: 'okt.', + 11: 'nov.', + 12: 'des.', + }, + 'narrow': { + 1: 'J', + 2: 'F', + 3: 'M', + 4: 'A', + 5: 'M', + 6: 'J', + 7: 'J', + 8: 'A', + 9: 'S', + 10: 'O', + 11: 'N', + 12: 'D', + }, + 'wide': { + 1: 'januar', + 2: 'februar', + 3: 'mars', + 4: 'april', + 5: 'mai', + 6: 'juni', + 7: 'juli', + 8: 'august', + 9: 'september', + 10: 'oktober', + 11: 'november', + 12: 'desember', + }, + }, + 'units': { + 'year': {'one': '{0} år', 'other': '{0} år'}, + 'month': {'one': '{0} månad', 'other': '{0} månadar'}, + 'week': {'one': '{0} veke', 'other': '{0} veker'}, + 'day': {'one': '{0} dag', 'other': '{0} dagar'}, + 'hour': {'one': '{0} time', 'other': '{0} timar'}, + 'minute': {'one': '{0} minutt', 'other': '{0} minutt'}, + 'second': {'one': '{0} sekund', 'other': '{0} sekund'}, + 'microsecond': { + 'one': '{0} mikrosekund', + 'other': '{0} mikrosekund', + }, + }, + 'relative': { + 'year': { + 'future': {'other': 'om {0} år', 'one': 'om {0} år'}, + 'past': { + 'other': 'for {0} år sidan', + 'one': 'for {0} år sidan', + }, + }, + 'month': { + 'future': {'other': 'om {0} månadar', 'one': 'om {0} månad'}, + 'past': { + 'other': 'for {0} månadar sidan', + 'one': 'for {0} månad sidan', + }, + }, + 'week': { + 'future': {'other': 'om {0} veker', 'one': 'om {0} veke'}, + 'past': { + 'other': 'for {0} veker sidan', + 'one': 'for {0} veke sidan', + }, + }, + 'day': { + 'future': {'other': 'om {0} dagar', 'one': 'om {0} dag'}, + 'past': { + 'other': 'for {0} dagar sidan', + 'one': 'for {0} dag sidan', + }, + }, + 'hour': { + 'future': {'other': 'om {0} timar', 'one': 'om {0} time'}, + 'past': { + 'other': 'for {0} timar sidan', + 'one': 'for {0} time sidan', + }, + }, + 'minute': { + 'future': {'other': 'om {0} minutt', 'one': 'om {0} minutt'}, + 'past': { + 'other': 'for {0} minutt sidan', + 'one': 'for {0} minutt sidan', + }, + }, + 'second': { + 'future': {'other': 'om {0} sekund', 'one': 'om {0} sekund'}, + 'past': { + 'other': 'for {0} sekund sidan', + 'one': 'for {0} sekund sidan', + }, + }, + }, + 'day_periods': {'am': 'formiddag', 'pm': 'ettermiddag'}, + }, + 'custom': custom_translations, +} diff --git a/tests/localization/test_nn.py b/tests/localization/test_nn.py new file mode 100644 index 00000000..a71146ba --- /dev/null +++ b/tests/localization/test_nn.py @@ -0,0 +1,88 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +import pendulum + + +locale = "nn" + + +def test_diff_for_humans(): + with pendulum.test(pendulum.datetime(2016, 8, 29)): + diff_for_humans() + + +def diff_for_humans(): + d = pendulum.now().subtract(seconds=1) + assert d.diff_for_humans(locale=locale) == "for 1 sekund sidan" + + d = pendulum.now().subtract(seconds=2) + assert d.diff_for_humans(locale=locale) == "for 2 sekund sidan" + + d = pendulum.now().subtract(minutes=1) + assert d.diff_for_humans(locale=locale) == "for 1 minutt sidan" + + d = pendulum.now().subtract(minutes=2) + assert d.diff_for_humans(locale=locale) == "for 2 minutt sidan" + + d = pendulum.now().subtract(hours=1) + assert d.diff_for_humans(locale=locale) == "for 1 time sidan" + + d = pendulum.now().subtract(hours=2) + assert d.diff_for_humans(locale=locale) == "for 2 timar sidan" + + d = pendulum.now().subtract(days=1) + assert d.diff_for_humans(locale=locale) == "for 1 dag sidan" + + d = pendulum.now().subtract(days=2) + assert d.diff_for_humans(locale=locale) == "for 2 dagar sidan" + + d = pendulum.now().subtract(weeks=1) + assert d.diff_for_humans(locale=locale) == "for 1 veke sidan" + + d = pendulum.now().subtract(weeks=2) + assert d.diff_for_humans(locale=locale) == "for 2 veker sidan" + + d = pendulum.now().subtract(months=1) + assert d.diff_for_humans(locale=locale) == "for 1 månad sidan" + + d = pendulum.now().subtract(months=2) + assert d.diff_for_humans(locale=locale) == "for 2 månadar sidan" + + d = pendulum.now().subtract(years=1) + assert d.diff_for_humans(locale=locale) == "for 1 år sidan" + + d = pendulum.now().subtract(years=2) + assert d.diff_for_humans(locale=locale) == "for 2 år sidan" + + d = pendulum.now().add(seconds=1) + assert d.diff_for_humans(locale=locale) == "om 1 sekund" + + d = pendulum.now().add(seconds=1) + d2 = pendulum.now() + assert d.diff_for_humans(d2, locale=locale) == "1 sekund etter" + assert d2.diff_for_humans(d, locale=locale) == "1 sekund før" + + assert d.diff_for_humans(d2, True, locale=locale) == "1 sekund" + assert ( + d2.diff_for_humans(d.add(seconds=1), True, locale=locale) == "2 sekund" + ) + + +def test_format(): + d = pendulum.datetime(2016, 8, 29, 7, 3, 6, 123456) + assert d.format("dddd", locale=locale) == "måndag" + assert d.format("ddd", locale=locale) == "mån." + assert d.format("MMMM", locale=locale) == "august" + assert d.format("MMM", locale=locale) == "aug." + assert d.format("A", locale=locale) == "formiddag" + assert d.format("Qo", locale=locale) == "3." + assert d.format("Mo", locale=locale) == "8." + assert d.format("Do", locale=locale) == "29." + + assert d.format("LT", locale=locale) == "07:03" + assert d.format("LTS", locale=locale) == "07:03:06" + assert d.format("L", locale=locale) == "29.08.2016" + assert d.format("LL", locale=locale) == "29. august 2016" + assert d.format("LLL", locale=locale) == "29. august 2016 07:03" + assert d.format("LLLL", locale=locale) == "måndag 29. august 2016 07:03" From 11e3fe2bb4c49cca161ef91021f2076b87abe905 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A5l=20Nilsen?= Date: Fri, 14 Sep 2018 14:19:02 +0200 Subject: [PATCH 048/256] Reformat with Black --- pendulum/locales/nb/locale.py | 247 ++++++++++++++++------------------ pendulum/locales/nn/locale.py | 231 +++++++++++++++---------------- tests/localization/test_nb.py | 5 +- tests/localization/test_nn.py | 4 +- 4 files changed, 226 insertions(+), 261 deletions(-) diff --git a/pendulum/locales/nb/locale.py b/pendulum/locales/nb/locale.py index 732b875d..1e55ac03 100644 --- a/pendulum/locales/nb/locale.py +++ b/pendulum/locales/nb/locale.py @@ -11,157 +11,142 @@ locale = { - 'plural': lambda n: 'one' if (n == n and ((n == 1))) else 'other', - 'ordinal': lambda n: 'other', - 'translations': { - 'days': { - 'abbreviated': { - 0: 'søn.', - 1: 'man.', - 2: 'tir.', - 3: 'ons.', - 4: 'tor.', - 5: 'fre.', - 6: 'lør.', + "plural": lambda n: "one" if (n == n and ((n == 1))) else "other", + "ordinal": lambda n: "other", + "translations": { + "days": { + "abbreviated": { + 0: "søn.", + 1: "man.", + 2: "tir.", + 3: "ons.", + 4: "tor.", + 5: "fre.", + 6: "lør.", }, - 'narrow': {0: 'S', 1: 'M', 2: 'T', 3: 'O', 4: 'T', 5: 'F', 6: 'L'}, - 'short': { - 0: 'sø.', - 1: 'ma.', - 2: 'ti.', - 3: 'on.', - 4: 'to.', - 5: 'fr.', - 6: 'lø.', + "narrow": {0: "S", 1: "M", 2: "T", 3: "O", 4: "T", 5: "F", 6: "L"}, + "short": { + 0: "sø.", + 1: "ma.", + 2: "ti.", + 3: "on.", + 4: "to.", + 5: "fr.", + 6: "lø.", }, - 'wide': { - 0: 'søndag', - 1: 'mandag', - 2: 'tirsdag', - 3: 'onsdag', - 4: 'torsdag', - 5: 'fredag', - 6: 'lørdag', + "wide": { + 0: "søndag", + 1: "mandag", + 2: "tirsdag", + 3: "onsdag", + 4: "torsdag", + 5: "fredag", + 6: "lørdag", }, }, - 'months': { - 'abbreviated': { - 1: 'jan.', - 2: 'feb.', - 3: 'mar.', - 4: 'apr.', - 5: 'mai', - 6: 'jun.', - 7: 'jul.', - 8: 'aug.', - 9: 'sep.', - 10: 'okt.', - 11: 'nov.', - 12: 'des.', + "months": { + "abbreviated": { + 1: "jan.", + 2: "feb.", + 3: "mar.", + 4: "apr.", + 5: "mai", + 6: "jun.", + 7: "jul.", + 8: "aug.", + 9: "sep.", + 10: "okt.", + 11: "nov.", + 12: "des.", }, - 'narrow': { - 1: 'J', - 2: 'F', - 3: 'M', - 4: 'A', - 5: 'M', - 6: 'J', - 7: 'J', - 8: 'A', - 9: 'S', - 10: 'O', - 11: 'N', - 12: 'D', + "narrow": { + 1: "J", + 2: "F", + 3: "M", + 4: "A", + 5: "M", + 6: "J", + 7: "J", + 8: "A", + 9: "S", + 10: "O", + 11: "N", + 12: "D", }, - 'wide': { - 1: 'januar', - 2: 'februar', - 3: 'mars', - 4: 'april', - 5: 'mai', - 6: 'juni', - 7: 'juli', - 8: 'august', - 9: 'september', - 10: 'oktober', - 11: 'november', - 12: 'desember', + "wide": { + 1: "januar", + 2: "februar", + 3: "mars", + 4: "april", + 5: "mai", + 6: "juni", + 7: "juli", + 8: "august", + 9: "september", + 10: "oktober", + 11: "november", + 12: "desember", }, }, - 'units': { - 'year': {'one': '{0} år', 'other': '{0} år'}, - 'month': {'one': '{0} måned', 'other': '{0} måneder'}, - 'week': {'one': '{0} uke', 'other': '{0} uker'}, - 'day': {'one': '{0} dag', 'other': '{0} dager'}, - 'hour': {'one': '{0} time', 'other': '{0} timer'}, - 'minute': {'one': '{0} minutt', 'other': '{0} minutter'}, - 'second': {'one': '{0} sekund', 'other': '{0} sekunder'}, - 'microsecond': { - 'one': '{0} mikrosekund', - 'other': '{0} mikrosekunder', - }, + "units": { + "year": {"one": "{0} år", "other": "{0} år"}, + "month": {"one": "{0} måned", "other": "{0} måneder"}, + "week": {"one": "{0} uke", "other": "{0} uker"}, + "day": {"one": "{0} dag", "other": "{0} dager"}, + "hour": {"one": "{0} time", "other": "{0} timer"}, + "minute": {"one": "{0} minutt", "other": "{0} minutter"}, + "second": {"one": "{0} sekund", "other": "{0} sekunder"}, + "microsecond": {"one": "{0} mikrosekund", "other": "{0} mikrosekunder"}, }, - 'relative': { - 'year': { - 'future': {'other': 'om {0} år', 'one': 'om {0} år'}, - 'past': { - 'other': 'for {0} år siden', - 'one': 'for {0} år siden', - }, + "relative": { + "year": { + "future": {"other": "om {0} år", "one": "om {0} år"}, + "past": {"other": "for {0} år siden", "one": "for {0} år siden"}, }, - 'month': { - 'future': {'other': 'om {0} måneder', 'one': 'om {0} måned'}, - 'past': { - 'other': 'for {0} måneder siden', - 'one': 'for {0} måned siden', + "month": { + "future": {"other": "om {0} måneder", "one": "om {0} måned"}, + "past": { + "other": "for {0} måneder siden", + "one": "for {0} måned siden", }, }, - 'week': { - 'future': {'other': 'om {0} uker', 'one': 'om {0} uke'}, - 'past': { - 'other': 'for {0} uker siden', - 'one': 'for {0} uke siden', - }, + "week": { + "future": {"other": "om {0} uker", "one": "om {0} uke"}, + "past": {"other": "for {0} uker siden", "one": "for {0} uke siden"}, }, - 'day': { - 'future': {'other': 'om {0} dager', 'one': 'om {0} dag'}, - 'past': { - 'other': 'for {0} dager siden', - 'one': 'for {0} dag siden', - }, + "day": { + "future": {"other": "om {0} dager", "one": "om {0} dag"}, + "past": {"other": "for {0} dager siden", "one": "for {0} dag siden"}, }, - 'hour': { - 'future': {'other': 'om {0} timer', 'one': 'om {0} time'}, - 'past': { - 'other': 'for {0} timer siden', - 'one': 'for {0} time siden', - }, + "hour": { + "future": {"other": "om {0} timer", "one": "om {0} time"}, + "past": {"other": "for {0} timer siden", "one": "for {0} time siden"}, }, - 'minute': { - 'future': {'other': 'om {0} minutter', 'one': 'om {0} minutt'}, - 'past': { - 'other': 'for {0} minutter siden', - 'one': 'for {0} minutt siden', + "minute": { + "future": {"other": "om {0} minutter", "one": "om {0} minutt"}, + "past": { + "other": "for {0} minutter siden", + "one": "for {0} minutt siden", }, }, - 'second': { - 'future': {'other': 'om {0} sekunder', 'one': 'om {0} sekund'}, - 'past': { - 'other': 'for {0} sekunder siden', - 'one': 'for {0} sekund siden', + "second": { + "future": {"other": "om {0} sekunder", "one": "om {0} sekund"}, + "past": { + "other": "for {0} sekunder siden", + "one": "for {0} sekund siden", }, }, }, - 'day_periods': { - 'midnight': 'midnatt', - 'am': 'a.m.', - 'pm': 'p.m.', - 'morning1': 'morgenen', - 'morning2': 'formiddagen', - 'afternoon1': 'ettermiddagen', - 'evening1': 'kvelden', - 'night1': 'natten', + "day_periods": { + "midnight": "midnatt", + "am": "a.m.", + "pm": "p.m.", + "morning1": "morgenen", + "morning2": "formiddagen", + "afternoon1": "ettermiddagen", + "evening1": "kvelden", + "night1": "natten", }, }, - 'custom': custom_translations, + "custom": custom_translations, } diff --git a/pendulum/locales/nn/locale.py b/pendulum/locales/nn/locale.py index 6bea19b7..172ad197 100644 --- a/pendulum/locales/nn/locale.py +++ b/pendulum/locales/nn/locale.py @@ -11,148 +11,133 @@ locale = { - 'plural': lambda n: 'one' if (n == n and ((n == 1))) else 'other', - 'ordinal': lambda n: 'other', - 'translations': { - 'days': { - 'abbreviated': { - 0: 'søn.', - 1: 'mån.', - 2: 'tys.', - 3: 'ons.', - 4: 'tor.', - 5: 'fre.', - 6: 'lau.', + "plural": lambda n: "one" if (n == n and ((n == 1))) else "other", + "ordinal": lambda n: "other", + "translations": { + "days": { + "abbreviated": { + 0: "søn.", + 1: "mån.", + 2: "tys.", + 3: "ons.", + 4: "tor.", + 5: "fre.", + 6: "lau.", }, - 'narrow': {0: 'S', 1: 'M', 2: 'T', 3: 'O', 4: 'T', 5: 'F', 6: 'L'}, - 'short': { - 0: 'sø.', - 1: 'må.', - 2: 'ty.', - 3: 'on.', - 4: 'to.', - 5: 'fr.', - 6: 'la.', + "narrow": {0: "S", 1: "M", 2: "T", 3: "O", 4: "T", 5: "F", 6: "L"}, + "short": { + 0: "sø.", + 1: "må.", + 2: "ty.", + 3: "on.", + 4: "to.", + 5: "fr.", + 6: "la.", }, - 'wide': { - 0: 'søndag', - 1: 'måndag', - 2: 'tysdag', - 3: 'onsdag', - 4: 'torsdag', - 5: 'fredag', - 6: 'laurdag', + "wide": { + 0: "søndag", + 1: "måndag", + 2: "tysdag", + 3: "onsdag", + 4: "torsdag", + 5: "fredag", + 6: "laurdag", }, }, - 'months': { - 'abbreviated': { - 1: 'jan.', - 2: 'feb.', - 3: 'mars', - 4: 'apr.', - 5: 'mai', - 6: 'juni', - 7: 'juli', - 8: 'aug.', - 9: 'sep.', - 10: 'okt.', - 11: 'nov.', - 12: 'des.', + "months": { + "abbreviated": { + 1: "jan.", + 2: "feb.", + 3: "mars", + 4: "apr.", + 5: "mai", + 6: "juni", + 7: "juli", + 8: "aug.", + 9: "sep.", + 10: "okt.", + 11: "nov.", + 12: "des.", }, - 'narrow': { - 1: 'J', - 2: 'F', - 3: 'M', - 4: 'A', - 5: 'M', - 6: 'J', - 7: 'J', - 8: 'A', - 9: 'S', - 10: 'O', - 11: 'N', - 12: 'D', + "narrow": { + 1: "J", + 2: "F", + 3: "M", + 4: "A", + 5: "M", + 6: "J", + 7: "J", + 8: "A", + 9: "S", + 10: "O", + 11: "N", + 12: "D", }, - 'wide': { - 1: 'januar', - 2: 'februar', - 3: 'mars', - 4: 'april', - 5: 'mai', - 6: 'juni', - 7: 'juli', - 8: 'august', - 9: 'september', - 10: 'oktober', - 11: 'november', - 12: 'desember', + "wide": { + 1: "januar", + 2: "februar", + 3: "mars", + 4: "april", + 5: "mai", + 6: "juni", + 7: "juli", + 8: "august", + 9: "september", + 10: "oktober", + 11: "november", + 12: "desember", }, }, - 'units': { - 'year': {'one': '{0} år', 'other': '{0} år'}, - 'month': {'one': '{0} månad', 'other': '{0} månadar'}, - 'week': {'one': '{0} veke', 'other': '{0} veker'}, - 'day': {'one': '{0} dag', 'other': '{0} dagar'}, - 'hour': {'one': '{0} time', 'other': '{0} timar'}, - 'minute': {'one': '{0} minutt', 'other': '{0} minutt'}, - 'second': {'one': '{0} sekund', 'other': '{0} sekund'}, - 'microsecond': { - 'one': '{0} mikrosekund', - 'other': '{0} mikrosekund', - }, + "units": { + "year": {"one": "{0} år", "other": "{0} år"}, + "month": {"one": "{0} månad", "other": "{0} månadar"}, + "week": {"one": "{0} veke", "other": "{0} veker"}, + "day": {"one": "{0} dag", "other": "{0} dagar"}, + "hour": {"one": "{0} time", "other": "{0} timar"}, + "minute": {"one": "{0} minutt", "other": "{0} minutt"}, + "second": {"one": "{0} sekund", "other": "{0} sekund"}, + "microsecond": {"one": "{0} mikrosekund", "other": "{0} mikrosekund"}, }, - 'relative': { - 'year': { - 'future': {'other': 'om {0} år', 'one': 'om {0} år'}, - 'past': { - 'other': 'for {0} år sidan', - 'one': 'for {0} år sidan', - }, + "relative": { + "year": { + "future": {"other": "om {0} år", "one": "om {0} år"}, + "past": {"other": "for {0} år sidan", "one": "for {0} år sidan"}, }, - 'month': { - 'future': {'other': 'om {0} månadar', 'one': 'om {0} månad'}, - 'past': { - 'other': 'for {0} månadar sidan', - 'one': 'for {0} månad sidan', + "month": { + "future": {"other": "om {0} månadar", "one": "om {0} månad"}, + "past": { + "other": "for {0} månadar sidan", + "one": "for {0} månad sidan", }, }, - 'week': { - 'future': {'other': 'om {0} veker', 'one': 'om {0} veke'}, - 'past': { - 'other': 'for {0} veker sidan', - 'one': 'for {0} veke sidan', - }, + "week": { + "future": {"other": "om {0} veker", "one": "om {0} veke"}, + "past": {"other": "for {0} veker sidan", "one": "for {0} veke sidan"}, }, - 'day': { - 'future': {'other': 'om {0} dagar', 'one': 'om {0} dag'}, - 'past': { - 'other': 'for {0} dagar sidan', - 'one': 'for {0} dag sidan', - }, + "day": { + "future": {"other": "om {0} dagar", "one": "om {0} dag"}, + "past": {"other": "for {0} dagar sidan", "one": "for {0} dag sidan"}, }, - 'hour': { - 'future': {'other': 'om {0} timar', 'one': 'om {0} time'}, - 'past': { - 'other': 'for {0} timar sidan', - 'one': 'for {0} time sidan', - }, + "hour": { + "future": {"other": "om {0} timar", "one": "om {0} time"}, + "past": {"other": "for {0} timar sidan", "one": "for {0} time sidan"}, }, - 'minute': { - 'future': {'other': 'om {0} minutt', 'one': 'om {0} minutt'}, - 'past': { - 'other': 'for {0} minutt sidan', - 'one': 'for {0} minutt sidan', + "minute": { + "future": {"other": "om {0} minutt", "one": "om {0} minutt"}, + "past": { + "other": "for {0} minutt sidan", + "one": "for {0} minutt sidan", }, }, - 'second': { - 'future': {'other': 'om {0} sekund', 'one': 'om {0} sekund'}, - 'past': { - 'other': 'for {0} sekund sidan', - 'one': 'for {0} sekund sidan', + "second": { + "future": {"other": "om {0} sekund", "one": "om {0} sekund"}, + "past": { + "other": "for {0} sekund sidan", + "one": "for {0} sekund sidan", }, }, }, - 'day_periods': {'am': 'formiddag', 'pm': 'ettermiddag'}, + "day_periods": {"am": "formiddag", "pm": "ettermiddag"}, }, - 'custom': custom_translations, + "custom": custom_translations, } diff --git a/tests/localization/test_nb.py b/tests/localization/test_nb.py index f9be7b58..3cd7e8ee 100644 --- a/tests/localization/test_nb.py +++ b/tests/localization/test_nb.py @@ -64,10 +64,7 @@ def diff_for_humans(): assert d2.diff_for_humans(d, locale=locale) == "1 sekund før" assert d.diff_for_humans(d2, True, locale=locale) == "1 sekund" - assert ( - d2.diff_for_humans(d.add(seconds=1), True, locale=locale) - == "2 sekunder" - ) + assert d2.diff_for_humans(d.add(seconds=1), True, locale=locale) == "2 sekunder" def test_format(): diff --git a/tests/localization/test_nn.py b/tests/localization/test_nn.py index a71146ba..5105c75c 100644 --- a/tests/localization/test_nn.py +++ b/tests/localization/test_nn.py @@ -64,9 +64,7 @@ def diff_for_humans(): assert d2.diff_for_humans(d, locale=locale) == "1 sekund før" assert d.diff_for_humans(d2, True, locale=locale) == "1 sekund" - assert ( - d2.diff_for_humans(d.add(seconds=1), True, locale=locale) == "2 sekund" - ) + assert d2.diff_for_humans(d.add(seconds=1), True, locale=locale) == "2 sekund" def test_format(): From e4cbd28dea51b35d3f81b5cf51c94d905e5a0a26 Mon Sep 17 00:00:00 2001 From: Carey Metcalfe Date: Sat, 25 Jan 2020 16:09:18 -0500 Subject: [PATCH 049/256] Fix RecursionError when adding to a DateTime with a FixedTimezone (#431) This seems to have been caused by `bpo-32417`. Before that change, adding a timedelta to a date/datetime subclass would always return an instance of date/datetime instead of the subclass. After the change, the subclass is preserved. The RecursionError was caused by adding a timedelta to a DateTime. Doing this uses the `convert` method of the DateTime's timezone to convert the new DateTime into the correct timezone. In the case of FixedTimezones, this requires adding the UTC offset of the timezone (a timedelta) to the DateTime, causing the recursion. Before bpo-32417, the subclass of the DateTime was dropped while calling `astimezone`. This meant that the object that was passed into `fromutc` by `astimezone` was a stdlib datetime, not a Pendulum DateTime. Calling the stdlib datetime's add function would then do the addition and return the result (which would then be upconverted back into a Pendulum DateTime instance). Now, due to the subclass being preserved, the Pendulum DateTime's add function is being called instead, causing the recursion. This commit fixes the RecursionError by always using the stdlib datetime's addition function to add the offset to the DateTime when calling fromutc. bpo-32417: https://bugs.python.org/issue32417 commit: https://github.com/python/cpython/commit/89427cd0feae25bbc8693abdccfa6a8c81a2689c Fixes #422 --- pendulum/tz/timezone.py | 3 ++- tests/datetime/test_add.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/pendulum/tz/timezone.py b/pendulum/tz/timezone.py index a79242ed..d339cdf7 100644 --- a/pendulum/tz/timezone.py +++ b/pendulum/tz/timezone.py @@ -342,7 +342,8 @@ def dst(self, dt): # type: Optional[datetime] # type: (...) -> timedelta return timedelta() def fromutc(self, dt): # type: (datetime) -> datetime - return (dt + self._utcoffset).replace(tzinfo=self) + # Use the stdlib datetime's add method to avoid infinite recursion + return (datetime.__add__(dt, self._utcoffset)).replace(tzinfo=self) def tzname(self, dt): # type: Optional[datetime] # type: (...) -> Union[str, None] return self._name diff --git a/tests/datetime/test_add.py b/tests/datetime/test_add.py index ab8c58e0..38fc7055 100644 --- a/tests/datetime/test_add.py +++ b/tests/datetime/test_add.py @@ -134,8 +134,9 @@ def test_addition_invalid_type(): def test_add_to_fixed_timezones(): dt = pendulum.parse("2015-03-08T01:00:00-06:00") dt = dt.add(weeks=1) + dt = dt.add(hours=1) - assert_datetime(dt, 2015, 3, 15, 1, 0, 0) + assert_datetime(dt, 2015, 3, 15, 2, 0, 0) assert dt.timezone_name == "-06:00" assert dt.offset == -6 * 3600 From cd9337558aea0b30054eca01d5f7a3f74d778c78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Eustace?= Date: Fri, 6 Mar 2020 10:17:00 +0100 Subject: [PATCH 050/256] Setup Github actions (#404) * Setup Github actions * Add Github templates --- .github/ISSUE_TEMPLATE/---bug-report.md | 31 + .github/ISSUE_TEMPLATE/---documentation.md | 22 + .github/ISSUE_TEMPLATE/---everything-else.md | 19 + .github/ISSUE_TEMPLATE/---feature-request.md | 23 + .github/PULL_REQUEST_TEMPLATE.md | 15 + .github/workflows/release.yml | 132 ++ .github/workflows/tests.yml | 141 +++ .gitignore | 2 - .travis.yml | 82 -- Makefile | 21 +- appveyor.yml | 33 - build-wheels.sh | 46 +- docs/docs/addition_subtraction.md | 6 - pendulum/datetime.py | 7 +- pendulum/parsing/_iso8601.c | 4 + poetry.lock | 1147 ++++++++++++++++++ pyproject.toml | 2 +- 17 files changed, 1560 insertions(+), 173 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/---bug-report.md create mode 100644 .github/ISSUE_TEMPLATE/---documentation.md create mode 100644 .github/ISSUE_TEMPLATE/---everything-else.md create mode 100644 .github/ISSUE_TEMPLATE/---feature-request.md create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 .github/workflows/release.yml create mode 100644 .github/workflows/tests.yml delete mode 100644 .travis.yml delete mode 100644 appveyor.yml create mode 100644 poetry.lock diff --git a/.github/ISSUE_TEMPLATE/---bug-report.md b/.github/ISSUE_TEMPLATE/---bug-report.md new file mode 100644 index 00000000..1f8b6180 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/---bug-report.md @@ -0,0 +1,31 @@ +--- +name: "\U0001F41E Bug Report" +about: Did you find a bug? +title: '' +labels: 'Bug' +assignees: '' + +--- + + + + +- [ ] I am on the [latest](https://github.com/sdispater/pendulum/releases/latest) Pendulum version. +- [ ] I have searched the [issues](https://github.com/sdispater/pendulum/issues) of this repo and believe that this is not a duplicate. + + + +- **OS version and name**: +- **Pendulum version**: + +## Issue + diff --git a/.github/ISSUE_TEMPLATE/---documentation.md b/.github/ISSUE_TEMPLATE/---documentation.md new file mode 100644 index 00000000..0d4ac414 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/---documentation.md @@ -0,0 +1,22 @@ +--- +name: "\U0001F4DA Documentation" +about: Did you find errors, problems, or anything unintelligible in the docs (https://pendulum.eustace.io/docs)? +title: '' +labels: 'Documentation' +assignees: '' + +--- + + + + +- [ ] I have searched the [issues](https://github.com/sdispater/pendulum/issues) of this repo and believe that this is not a duplicate. + +## Issue + diff --git a/.github/ISSUE_TEMPLATE/---everything-else.md b/.github/ISSUE_TEMPLATE/---everything-else.md new file mode 100644 index 00000000..1fc60fac --- /dev/null +++ b/.github/ISSUE_TEMPLATE/---everything-else.md @@ -0,0 +1,19 @@ +--- +name: "\U0001F5C3 Everything Else" +about: For questions and issues that do not fall in any of the other categories. This + can include questions about Pendulum's roadmap. +title: '' +labels: '' +assignees: '' + +--- + + +- [ ] I have searched the [issues](https://github.com/sdispater/pendulum/issues) of this repo and believe that this is not a duplicate. +- [ ] I have searched the [documentation](https://pendulum.eustace.io/docs/) and believe that my question is not covered. + +## Issue + diff --git a/.github/ISSUE_TEMPLATE/---feature-request.md b/.github/ISSUE_TEMPLATE/---feature-request.md new file mode 100644 index 00000000..46050556 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/---feature-request.md @@ -0,0 +1,23 @@ +--- +name: "\U0001F381 Feature Request" +about: Do you have ideas for new features and improvements? +title: '' +labels: 'Feature' +assignees: '' + +--- + + + + +- [ ] I have searched the [issues](https://github.com/sdispater/pendulum/issues) of this repo and believe that this is not a duplicate. +- [ ] I have searched the [documentation](https://pendulum.eustace.io/docs/) and believe that my question is not covered. + +## Feature Request + diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000..4c0ce41a --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,15 @@ +## Pull Request Check List + + + +- [ ] Added **tests** for changed code. +- [ ] Updated **documentation** for changed code. + + diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..3dc1bf57 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,132 @@ +name: Release + +on: + push: + tags: + - '*.*.*' + +jobs: + + Linux: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Get tag + id: tag + run: | + echo ::set-output name=tag::${GITHUB_REF#refs/tags/} + - name: Building release + run: | + make linux_release + - name: Upload distributions artifacts + uses: actions/upload-artifact@v1 + with: + name: pendulum-dist + path: dist/wheelhouse + + MacOS: + runs-on: macos-latest + strategy: + matrix: + python-version: [2.7, 3.5, 3.6, 3.7, 3.8] + + steps: + - uses: actions/checkout@v2 + - name: Get tag + id: tag + run: | + echo ::set-output name=tag::${GITHUB_REF#refs/tags/} + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v1 + with: + python-version: ${{ matrix.python-version }} + - 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 --preview -y + - name: Build distributions + run: | + source $HOME/.poetry/env + poetry build -vvv + - name: Upload distribution artifacts + uses: actions/upload-artifact@v1 + with: + name: pendulum-dist + path: dist + + Windows: + runs-on: windows-latest + strategy: + matrix: + python-version: [2.7, 3.5, 3.6, 3.7, 3.8] + + steps: + - uses: actions/checkout@v2 + - name: Get tag + id: tag + shell: bash + run: | + echo ::set-output name=tag::${GITHUB_REF#refs/tags/} + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v1 + with: + python-version: ${{ matrix.python-version }} + - 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 --preview -y + - name: Build distributions + run: | + $env:Path += ";$env:Userprofile\.poetry\bin" + poetry build -vvv + - name: Upload distribution artifact + uses: actions/upload-artifact@v1 + with: + name: pendulum-dist + path: dist + + Release: + needs: [Linux, MacOS, Windows] + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v2 + - name: Get tag + id: tag + run: | + echo ::set-output name=tag::${GITHUB_REF#refs/tags/} + - name: Download distribution artifact + uses: actions/download-artifact@master + with: + name: pendulum-dist + path: dist + - 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 --preview -y + - 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: Check distributions + run: | + ls -la dist + - name: Publish to PyPI + env: + POETRY_PYPI_TOKEN_PYPI: ${{ secrets.PYPI_TOKEN }} + run: | + source $HOME/.poetry/env + poetry publish + - name: Create Release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }} + with: + tag_name: ${{ steps.tag.outputs.tag }} + release_name: ${{ steps.tag.outputs.tag }} + draft: false + prerelease: false diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 00000000..fa743c1e --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,141 @@ +name: Tests + +on: [push, pull_request] + +jobs: + Linting: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Set up Python 3.8 + uses: actions/setup-python@v1 + with: + python-version: 3.8 + - name: Linting + run: | + pip install pre-commit + pre-commit run --all-files + Linux: + needs: Linting + runs-on: ubuntu-latest + strategy: + matrix: + python-version: [2.7, 3.5, 3.6, 3.7, 3.8, pypy2, 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 --preview -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: Install dependencies + run: | + source $HOME/.poetry/env + poetry install + - name: Test + run: | + source $HOME/.poetry/env + poetry run pytest -q tests + poetry install + - name: Test Pure Python + run: | + source $HOME/.poetry/env + PENDULUM_EXTENSIONS=0 poetry run pytest -q tests + MacOS: + needs: Linting + runs-on: macos-latest + strategy: + matrix: + python-version: [2.7, 3.5, 3.6, 3.7, 3.8, pypy2, 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 --preview -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: Install dependencies + run: | + source $HOME/.poetry/env + poetry install + - name: Test + run: | + source $HOME/.poetry/env + poetry run pytest -q tests + - name: Test Pure Python + run: | + source $HOME/.poetry/env + PENDULUM_EXTENSIONS=0 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] + + 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 --preview -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: Install dependencies + run: | + $env:Path += ";$env:Userprofile\.poetry\bin" + poetry install + - name: Test + run: | + $env:Path += ";$env:Userprofile\.poetry\bin" + poetry run pytest -q tests + - name: Test Pure Python + run: | + $env:Path += ";$env:Userprofile\.poetry\bin" + $env:PENDULUM_EXTENSIONS = "0" + poetry run pytest -q tests diff --git a/.gitignore b/.gitignore index dcd8ed7d..bb25f8b3 100644 --- a/.gitignore +++ b/.gitignore @@ -30,8 +30,6 @@ results.json profile.html /wheelhouse /docs/site/* -pyproject.lock -/poetry.lock setup.py # editor diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index e7baefe1..00000000 --- a/.travis.yml +++ /dev/null @@ -1,82 +0,0 @@ -language: python - -stages: - - linting - - test - -cache: - pip: true - directories: - - $HOME/.cache/pypoetry - -jobs: - fast_finish: true - include: - - python: 2.7 - env: PENDULUM_EXTENSIONS=1 - - python: 2.7 - env: PENDULUM_EXTENSIONS=0 - - python: 3.4 - env: PENDULUM_EXTENSIONS=1 - - python: 3.4 - env: PENDULUM_EXTENSIONS=0 - - python: 3.5 - env: PENDULUM_EXTENSIONS=1 - - python: 3.5 - env: PENDULUM_EXTENSIONS=0 - - python: 3.6 - env: PENDULUM_EXTENSIONS=1 - - python: 3.6 - env: PENDULUM_EXTENSIONS=0 - - python: 3.7 - dist: xenial - env: PENDULUM_EXTENSIONS=1 - - python: 3.7 - dist: xenial - env: PENDULUM_EXTENSIONS=0 - - python: pypy - - python: pypy3 - - - stage: linting - python: "3.6" - install: - - pip install pre-commit - - pre-commit install-hooks - script: - - pre-commit run --all-files - allow_failures: - - python: pypy - - python: pypy3 - -before_install: - - pip install codecov - -install: - - | - if [ "$TRAVIS_PYTHON_VERSION" = "pypy" ]; then - export PYENV_ROOT="$HOME/.pyenv" - if [ -f "$PYENV_ROOT/bin/pyenv" ]; then - pushd "$PYENV_ROOT" && git pull && popd - else - rm -rf "$PYENV_ROOT" && git clone --depth 1 https://github.com/yyuu/pyenv.git "$PYENV_ROOT" - fi - export PYPY_VERSION="5.6.0" - "$PYENV_ROOT/bin/pyenv" install --skip-existing "pypy-$PYPY_VERSION" - virtualenv --python="$PYENV_ROOT/versions/pypy-$PYPY_VERSION/bin/python" "$HOME/virtualenvs/pypy-$PYPY_VERSION" - source "$HOME/virtualenvs/pypy-$PYPY_VERSION/bin/activate" - fi - - pip install pip -U - - curl -fsS -o get-poetry.py https://raw.githubusercontent.com/sdispater/poetry/master/get-poetry.py - - python get-poetry.py --preview -y - - source $HOME/.poetry/env - - poetry install -v - - poetry build -v - - | - if [ "$PENDULUM_EXTENSIONS" == "1" ]; then - find dist/ -iname pendulum*.whl -exec unzip -o {} 'pendulum/*' -d . \; - fi - -script: poetry run pytest --cov=pendulum --cov-config=.coveragerc tests/ -W ignore - -after_success: - - codecov diff --git a/Makefile b/Makefile index a1677b48..e68b94c1 100644 --- a/Makefile +++ b/Makefile @@ -23,7 +23,9 @@ setup: setup-python test: @py.test --cov=pendulum --cov-config .coveragerc tests/ -sq -release: wheels_x64 cp_wheels_x64 wheel +linux_release: wheels_x64 wheels_i686 + +release: wheels_x64 wheels_i686 wheel publish: @poetry publish --no-build @@ -34,31 +36,18 @@ tar: wheel: @poetry build -v -wheels_x64: clean_wheels build_wheels_x64 +wheels_x64: build_wheels_x64 -wheels_i686: clean_wheels build_wheels_i686 +wheels_i686: build_wheels_i686 build_wheels_x64: - rm -rf wheelhouse/ - mkdir wheelhouse docker pull quay.io/pypa/manylinux1_x86_64 docker run --rm -v `pwd`:/io quay.io/pypa/manylinux1_x86_64 /io/build-wheels.sh build_wheels_i686: - rm -rf wheelhouse/ - mkdir wheelhouse docker pull quay.io/pypa/manylinux1_i686 docker run --rm -v `pwd`:/io quay.io/pypa/manylinux1_i686 /io/build-wheels.sh -clean_wheels: - rm -rf wheelhouse/ - -cp_wheels_x64: - mv wheelhouse/*manylinux1_x86_64.whl dist/ - -cp_wheels_i686: - mv wheelhouse/*manylinux1_i686.whl dist/ - # run tests against all supported python versions tox: @tox diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index adf929b7..00000000 --- a/appveyor.yml +++ /dev/null @@ -1,33 +0,0 @@ -build: false - -environment: - matrix: - - PYTHON: "C:/Python27" - - PYTHON: "C:/Python27-x64" - - PYTHON: "C:/Python36" - - PYTHON: "C:/Python36-x64" - -cache: - - '%LocalAppData%\pip\Cache' - - '%LocalAppData%\pypoetry\Cache' - - -install: - - "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%" - - # Upgrade to the latest version of pip to avoid it displaying warnings - # about it being out of date. - - "python -m pip install --disable-pip-version-check --user --upgrade pip" - - # Downloading and installing poetry - - python -m pip install poetry>=1.0.0b1 -U - - # Install dependencies - - python -m pip install codecov - - poetry install -v - -test_script: - - "poetry run pytest --cov=pendulum --cov-config=.coveragerc tests/ -W ignore" - -after_test: - - "codecov" diff --git a/build-wheels.sh b/build-wheels.sh index 1ae9eed1..8e132dcc 100755 --- a/build-wheels.sh +++ b/build-wheels.sh @@ -1,38 +1,22 @@ #!/bin/bash -PYTHON_VERSIONS="cp27-cp27m cp34-cp34m cp35-cp35m cp36-cp36m cp37-cp37m" +set -e -x -POETRY_PYTHON="cp37-cp37m" -POETRY_VENV="/opt/python/poetry" -echo "Create Poetry's virtualenv" -/opt/python/${POETRY_PYTHON}/bin/pip install virtualenv -/opt/python/${POETRY_PYTHON}/bin/virtualenv --python /opt/python/${POETRY_PYTHON}/bin/python ${POETRY_VENV} -${POETRY_VENV}/bin/pip install poetry --pre +cd $(dirname $0) -RELEASE=$(sed -n "s/__version__ = \"\(.*\)\"/\1/p" /io/pendulum/__version__.py) +curl -fsS -o get-poetry.py https://raw.githubusercontent.com/sdispater/poetry/master/get-poetry.py +/opt/python/cp38-cp38/bin/python get-poetry.py --preview -y +rm get-poetry.py -echo "Compile wheels" -for PYTHON in ${PYTHON_VERSIONS}; do - cd /io - /opt/python/${POETRY_PYTHON}/bin/virtualenv --python /opt/python/${PYTHON}/bin/python /opt/python/venv-${PYTHON} - . /opt/python/venv-${PYTHON}/bin/activate - ${POETRY_VENV}/bin/poetry install -v - ${POETRY_VENV}/bin/poetry build -v - mv dist/*-${RELEASE}-*-linux_*.whl wheelhouse/ - deactivate - cd - +for PYBIN in /opt/python/*/bin; do + if [ "$PYBIN" == "/opt/python/cp34-cp34m/bin" ]; then + continue + fi + rm -rf build + "${PYBIN}/python" $HOME/.poetry/bin/poetry build -vvv done -echo "Bundle external shared libraries into the wheels" -for whl in /io/wheelhouse/pendulum-${RELEASE}-*-linux_*.whl; do - auditwheel repair "$whl" -w /io/wheelhouse/ -done - -echo "Install packages and test" -for PYTHON in ${PYTHON_VERSIONS}; do - . /opt/python/venv-${PYTHON}/bin/activate - pip install pendulum==${RELEASE} --no-index --find-links /io/wheelhouse - find ./io/tests | grep -E "(__pycache__|\.pyc$)" | xargs rm -rf - pytest /io/tests -W ignore - find ./io/tests | grep -E "(__pycache__|\.pyc$)" | xargs rm -rf - deactivate +cd dist +for whl in *.whl; do + auditwheel repair "$whl" + rm "$whl" done diff --git a/docs/docs/addition_subtraction.md b/docs/docs/addition_subtraction.md index cd55a96c..87626a13 100644 --- a/docs/docs/addition_subtraction.md +++ b/docs/docs/addition_subtraction.md @@ -79,12 +79,6 @@ Each method returns a new `DateTime` instance. '2015-04-03 12:31:43' >>> dt = dt.subtract(years=3, months=2, days=6, hours=12, minutes=31, seconds=43) '2012-01-28 00:00:00' - -# You can also add or remove a timedelta ->>> dt.add_timedelta(timedelta(hours=3, minutes=4, seconds=5)) -'2012-01-28 03:04:05' ->>> dt.sub_timedelta(timedelta(hours=3, minutes=4, seconds=5)) -'2012-01-28 00:00:00' ``` !!!note diff --git a/pendulum/datetime.py b/pendulum/datetime.py index abca24a8..b9c3e09b 100644 --- a/pendulum/datetime.py +++ b/pendulum/datetime.py @@ -722,7 +722,10 @@ def subtract( microseconds=-microseconds, ) - def _add_timedelta(self, delta): + # 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): """ Add timedelta duration to the instance. @@ -1439,7 +1442,7 @@ def __add__(self, other): if not isinstance(other, datetime.timedelta): return NotImplemented - return self._add_timedelta(other) + return self._add_timedelta_(other) def __radd__(self, other): return self.__add__(other) diff --git a/pendulum/parsing/_iso8601.c b/pendulum/parsing/_iso8601.c index a81f25aa..c44f5100 100644 --- a/pendulum/parsing/_iso8601.c +++ b/pendulum/parsing/_iso8601.c @@ -659,6 +659,10 @@ Parsed* _parse_iso8601_datetime(char *str, Parsed *parsed) { // which is invalid for a date // But it might be a time in the form hhmmss parsed->ambiguous = 1; + } else if (separators > 1) { + parsed->error = PARSER_INVALID_DATE; + + return NULL; } parsed->month = monthday; diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 00000000..f1efe611 --- /dev/null +++ b/poetry.lock @@ -0,0 +1,1147 @@ +[[package]] +category = "dev" +description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +name = "appdirs" +optional = false +python-versions = "*" +version = "1.4.3" + +[[package]] +category = "dev" +description = "A few extensions to pyyaml." +name = "aspy.yaml" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "1.3.0" + +[package.dependencies] +pyyaml = "*" + +[[package]] +category = "dev" +description = "Atomic file writes." +name = "atomicwrites" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "1.3.0" + +[[package]] +category = "dev" +description = "Classes Without Boilerplate" +name = "attrs" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "19.3.0" + +[package.extras] +azure-pipelines = ["coverage", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "pytest-azurepipelines"] +dev = ["coverage", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "sphinx", "pre-commit"] +docs = ["sphinx", "zope.interface"] +tests = ["coverage", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"] + +[[package]] +category = "dev" +description = "Internationalization utilities" +name = "babel" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "2.8.0" + +[package.dependencies] +pytz = ">=2015.7" + +[[package]] +category = "dev" +description = "The uncompromising code formatter." +marker = "python_version >= \"3.6\" and python_version < \"4.0\"" +name = "black" +optional = false +python-versions = ">=3.6" +version = "19.10b0" + +[package.dependencies] +appdirs = "*" +attrs = ">=18.1.0" +click = ">=6.5" +pathspec = ">=0.6,<1" +regex = "*" +toml = ">=0.9.4" +typed-ast = ">=1.4.0" + +[package.extras] +d = ["aiohttp (>=3.3.2)", "aiohttp-cors"] + +[[package]] +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 = "*" + +[[package]] +category = "dev" +description = "Cleo allows you to create beautiful and testable command-line interfaces." +name = "cleo" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "0.7.6" + +[package.dependencies] +clikit = ">=0.4.0,<0.5.0" + +[[package]] +category = "dev" +description = "Composable command line interface toolkit" +marker = "python_version >= \"3.6\" and python_version < \"4.0\"" +name = "click" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "7.0" + +[[package]] +category = "dev" +description = "CliKit is a group of utilities to build beautiful and testable command line interfaces." +name = "clikit" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "0.4.2" + +[package.dependencies] +pastel = ">=0.2.0,<0.3.0" +pylev = ">=1.3,<2.0" + +[package.dependencies.enum34] +python = ">=2.7,<2.8" +version = ">=1.1,<2.0" + +[package.dependencies.typing] +python = ">=2.7,<2.8 || >=3.4,<3.5" +version = ">=3.6,<4.0" + +[package.dependencies.typing-extensions] +python = ">=3.5.0,<3.5.4" +version = ">=3.6,<4.0" + +[[package]] +category = "dev" +description = "Cross-platform colored terminal text." +marker = "sys_platform == \"win32\" and python_version == \"3.4\"" +name = "colorama" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "0.4.1" + +[[package]] +category = "dev" +description = "Cross-platform colored terminal text." +marker = "sys_platform == \"win32\" and python_version != \"3.4\" or platform_system == \"Windows\"" +name = "colorama" +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+." +marker = "python_version < \"3\"" +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" +marker = "python_version < \"3.4\"" +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" +name = "coverage" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, <4" +version = "4.5.4" + +[[package]] +category = "dev" +description = "Code coverage measurement for Python" +name = "coverage" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" +version = "5.0.3" + +[package.extras] +toml = ["toml"] + +[[package]] +category = "dev" +description = "Distribution utilities" +name = "distlib" +optional = false +python-versions = "*" +version = "0.3.0" + +[[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" +marker = "python_version >= \"2.7\" and python_version < \"2.8\"" +name = "enum34" +optional = false +python-versions = "*" +version = "1.1.9" + +[[package]] +category = "dev" +description = "A platform independent file lock." +name = "filelock" +optional = false +python-versions = "*" +version = "3.0.12" + +[[package]] +category = "dev" +description = "Python function signatures from PEP362 for Python 2.6, 2.7 and 3.2+" +marker = "python_version < \"3.0\"" +name = "funcsigs" +optional = false +python-versions = "*" +version = "1.0.2" + +[[package]] +category = "dev" +description = "Backport of the concurrent.futures package from Python 3" +marker = "python_version < \"3.2\"" +name = "futures" +optional = false +python-versions = ">=2.6, <3" +version = "3.3.0" + +[[package]] +category = "dev" +description = "File identification library for Python" +name = "identify" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" +version = "1.4.11" + +[package.extras] +license = ["editdistance"] + +[[package]] +category = "dev" +description = "Read metadata from Python packages" +name = "importlib-metadata" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" +version = "1.1.3" + +[package.dependencies] +zipp = ">=0.5" + +[package.extras] +docs = ["sphinx", "rst.linker"] +testing = ["packaging", "importlib-resources"] + +[[package]] +category = "dev" +description = "Read metadata from Python packages" +marker = "python_version < \"3.8\"" +name = "importlib-metadata" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" +version = "1.5.0" + +[package.dependencies] +zipp = ">=0.5" + +[package.dependencies.configparser] +python = "<3" +version = ">=3.5" + +[package.dependencies.contextlib2] +python = "<3" +version = "*" + +[package.dependencies.pathlib2] +python = "<3" +version = "*" + +[package.extras] +docs = ["sphinx", "rst.linker"] +testing = ["packaging", "importlib-resources"] + +[[package]] +category = "dev" +description = "Read resources from Python packages" +marker = "python_version < \"3.7\"" +name = "importlib-resources" +optional = false +python-versions = ">=2.7,!=3.0,!=3.1,!=3.2,!=3.3" +version = "1.0.2" + +[package.dependencies] +[package.dependencies.typing] +python = "<3.5" +version = "*" + +[[package]] +category = "dev" +description = "Read resources from Python packages" +marker = "python_version < \"3.7\"" +name = "importlib-resources" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" +version = "1.2.0" + +[package.dependencies] +[package.dependencies.importlib-metadata] +python = "<3.8" +version = "*" + +[package.dependencies.pathlib2] +python = "<3" +version = "*" + +[package.dependencies.singledispatch] +python = "<3.4" +version = "*" + +[package.dependencies.typing] +python = "<3.5" +version = "*" + +[package.dependencies.zipp] +python = "<3.8" +version = ">=0.4" + +[package.extras] +docs = ["sphinx", "docutils (0.12)", "rst.linker"] + +[[package]] +category = "dev" +description = "More routines for operating on iterables, beyond itertools" +marker = "python_version <= \"2.7\"" +name = "more-itertools" +optional = false +python-versions = "*" +version = "5.0.0" + +[package.dependencies] +six = ">=1.0.0,<2.0.0" + +[[package]] +category = "dev" +description = "More routines for operating on iterables, beyond itertools" +marker = "python_version > \"2.7\"" +name = "more-itertools" +optional = false +python-versions = ">=3.4" +version = "7.2.0" + +[[package]] +category = "dev" +description = "More routines for operating on iterables, beyond itertools" +marker = "python_version > \"2.7\"" +name = "more-itertools" +optional = false +python-versions = ">=3.5" +version = "8.2.0" + +[[package]] +category = "dev" +description = "Node.js virtual environment builder" +name = "nodeenv" +optional = false +python-versions = "*" +version = "1.3.5" + +[[package]] +category = "dev" +description = "Core utilities for Python packages" +name = "packaging" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "20.1" + +[package.dependencies] +pyparsing = ">=2.0.2" +six = "*" + +[[package]] +category = "dev" +description = "Bring colors to your terminal." +name = "pastel" +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" +marker = "python_version < \"3.6\" or python_version < \"3.4\" and sys_platform != \"win32\"" +name = "pathlib2" +optional = false +python-versions = "*" +version = "2.3.5" + +[package.dependencies] +six = "*" + +[package.dependencies.scandir] +python = "<3.5" +version = "*" + +[[package]] +category = "dev" +description = "Utility library for gitignore style pattern matching of file paths." +marker = "python_version >= \"3.6\" and python_version < \"4.0\"" +name = "pathspec" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +version = "0.7.0" + +[[package]] +category = "dev" +description = "plugin and hook calling mechanisms for python" +name = "pluggy" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "0.13.1" + +[package.dependencies] +[package.dependencies.importlib-metadata] +python = "<3.8" +version = ">=0.12" + +[package.extras] +dev = ["pre-commit", "tox"] + +[[package]] +category = "dev" +description = "A framework for managing and maintaining multi-language pre-commit hooks." +name = "pre-commit" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "1.18.3" + +[package.dependencies] +"aspy.yaml" = "*" +cfgv = ">=2.0.0" +identify = ">=1.0.0" +importlib-metadata = "*" +nodeenv = ">=0.11.1" +pyyaml = "*" +six = "*" +toml = "*" +virtualenv = ">=15.2" + +[package.dependencies.importlib-resources] +python = "<3.7" +version = "*" + +[[package]] +category = "dev" +description = "A framework for managing and maintaining multi-language pre-commit hooks." +name = "pre-commit" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" +version = "1.21.0" + +[package.dependencies] +"aspy.yaml" = "*" +cfgv = ">=2.0.0" +identify = ">=1.0.0" +nodeenv = ">=0.11.1" +pyyaml = "*" +six = "*" +toml = "*" +virtualenv = ">=15.2" + +[package.dependencies.futures] +python = "<3.2" +version = "*" + +[package.dependencies.importlib-metadata] +python = "<3.8" +version = "*" + +[package.dependencies.importlib-resources] +python = "<3.7" +version = "*" + +[[package]] +category = "dev" +description = "library with cross-python path, ini-parsing, io, code, log facilities" +name = "py" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "1.8.1" + +[[package]] +category = "dev" +description = "A pure Python Levenshtein implementation that's not freaking GPL'd." +name = "pylev" +optional = false +python-versions = "*" +version = "1.3.0" + +[[package]] +category = "dev" +description = "Python parsing module" +name = "pyparsing" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +version = "2.4.6" + +[[package]] +category = "dev" +description = "pytest: simple powerful testing with Python" +name = "pytest" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" +version = "4.6.9" + +[package.dependencies] +atomicwrites = ">=1.0" +attrs = ">=17.4.0" +packaging = "*" +pluggy = ">=0.12,<1.0" +py = ">=1.5.0" +six = ">=1.10.0" +wcwidth = "*" + +[[package.dependencies.colorama]] +python = "<3.4.0 || >=3.5.0" +version = "*" + +[[package.dependencies.colorama]] +python = ">=3.4,<3.5" +version = "<=0.4.1" + +[[package.dependencies.more-itertools]] +python = "<2.8" +version = ">=4.0.0,<6.0.0" + +[[package.dependencies.more-itertools]] +python = ">=2.8" +version = ">=4.0.0" + +[package.dependencies.funcsigs] +python = "<3.0" +version = ">=1.0" + +[package.dependencies.importlib-metadata] +python = "<3.8" +version = ">=0.12" + +[package.dependencies.pathlib2] +python = "<3.6" +version = ">=2.2.0" + +[package.extras] +testing = ["argcomplete", "hypothesis (>=3.56)", "nose", "requests", "mock"] + +[[package]] +category = "dev" +description = "Pytest plugin for measuring coverage." +name = "pytest-cov" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "2.8.1" + +[package.dependencies] +coverage = ">=4.4" +pytest = ">=3.6" + +[package.extras] +testing = ["fields", "hunter", "process-tests (2.0.2)", "six", "virtualenv"] + +[[package]] +category = "main" +description = "Extensions to the standard Python datetime module" +name = "python-dateutil" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +version = "2.8.1" + +[package.dependencies] +six = ">=1.5" + +[[package]] +category = "dev" +description = "World timezone definitions, modern and historical" +name = "pytz" +optional = false +python-versions = "*" +version = "2019.3" + +[[package]] +category = "main" +description = "The Olson timezone database for Python." +name = "pytzdata" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "2019.3" + +[[package]] +category = "dev" +description = "YAML parser and emitter for Python" +name = "pyyaml" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "5.2" + +[[package]] +category = "dev" +description = "YAML parser and emitter for Python" +name = "pyyaml" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +version = "5.3" + +[[package]] +category = "dev" +description = "Alternative regular expression module, to replace re." +marker = "python_version >= \"3.6\" and python_version < \"4.0\"" +name = "regex" +optional = false +python-versions = "*" +version = "2020.2.20" + +[[package]] +category = "dev" +description = "scandir, a better directory iterator and faster os.walk()" +marker = "python_version < \"3.5\" or python_version < \"3.4\" and sys_platform != \"win32\"" +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." +marker = "python_version < \"3.4\"" +name = "singledispatch" +optional = false +python-versions = "*" +version = "3.4.0.3" + +[package.dependencies] +six = "*" + +[[package]] +category = "main" +description = "Python 2 and 3 compatibility utilities" +name = "six" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +version = "1.14.0" + +[[package]] +category = "dev" +description = "Python Library for Tom's Obvious, Minimal Language" +name = "toml" +optional = false +python-versions = "*" +version = "0.10.0" + +[[package]] +category = "dev" +description = "tox is a generic virtualenv management and test command line tool" +name = "tox" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "3.12.1" + +[package.dependencies] +filelock = ">=3.0.0,<4" +pluggy = ">=0.3.0,<1" +py = ">=1.4.17,<2" +setuptools = ">=30.0.0" +six = ">=1.0.0,<2" +toml = ">=0.9.4" +virtualenv = ">=14.0.0" + +[package.extras] +docs = ["sphinx (>=2.0.0,<3)", "towncrier (>=18.5.0)", "pygments-github-lexers (>=0.0.5)", "sphinxcontrib-autoprogram (>=0.1.5)"] +testing = ["freezegun (>=0.3.11,<1)", "pathlib2 (>=2.3.3,<3)", "pytest (>=3.0.0,<5)", "pytest-cov (>=2.5.1,<3)", "pytest-mock (>=1.10.0,<2)", "pytest-xdist (>=1.22.2,<2)", "pytest-randomly (>=1.2.3,<2)", "flaky (>=3.4.0,<4)", "psutil (>=5.6.1,<6)"] + +[[package]] +category = "dev" +description = "tox is a generic virtualenv management and test command line tool" +name = "tox" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" +version = "3.14.5" + +[package.dependencies] +colorama = ">=0.4.1" +filelock = ">=3.0.0,<4" +packaging = ">=14" +pluggy = ">=0.12.0,<1" +py = ">=1.4.17,<2" +six = ">=1.14.0,<2" +toml = ">=0.9.4" +virtualenv = ">=16.0.0" + +[package.dependencies.importlib-metadata] +python = "<3.8" +version = ">=0.12,<2" + +[package.extras] +docs = ["sphinx (>=2.0.0,<3)", "towncrier (>=18.5.0)", "pygments-github-lexers (>=0.0.5)", "sphinxcontrib-autoprogram (>=0.1.5)"] +testing = ["freezegun (>=0.3.11,<1)", "pathlib2 (>=2.3.3,<3)", "pytest (>=4.0.0,<6)", "pytest-cov (>=2.5.1,<3)", "pytest-mock (>=1.10.0,<2)", "pytest-xdist (>=1.22.2,<2)", "pytest-randomly (>=1.0.0,<4)", "flaky (>=3.4.0,<4)", "psutil (>=5.6.1,<6)"] + +[[package]] +category = "dev" +description = "a fork of Python 2 and 3 ast modules with type comment support" +marker = "python_version >= \"3.6\" and python_version < \"4.0\"" +name = "typed-ast" +optional = false +python-versions = "*" +version = "1.4.1" + +[[package]] +category = "main" +description = "Type Hints for Python" +marker = "python_version >= \"2.7\" and python_version < \"2.8\" or python_version >= \"3.4\" and python_version < \"3.5\" or python_version < \"3.5\"" +name = "typing" +optional = false +python-versions = "*" +version = "3.7.4.1" + +[[package]] +category = "dev" +description = "Backported and Experimental Type Hints for Python 3.5+" +marker = "python_version >= \"3.5.0\" and python_version < \"3.5.4\"" +name = "typing-extensions" +optional = false +python-versions = "*" +version = "3.7.4.1" + +[[package]] +category = "dev" +description = "Virtual Python Environment builder" +name = "virtualenv" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" +version = "20.0.8" + +[package.dependencies] +appdirs = ">=1.4.3,<2" +distlib = ">=0.3.0,<1" +filelock = ">=3.0.0,<4" +six = ">=1.9.0,<2" + +[package.dependencies.contextlib2] +python = "<3.3" +version = ">=0.6.0,<1" + +[package.dependencies.importlib-metadata] +python = "<3.8" +version = ">=0.12,<2" + +[package.dependencies.importlib-resources] +python = "<3.7" +version = ">=1.0,<2" + +[package.dependencies.pathlib2] +python = "<3.4" +version = ">=2.3.3,<3" + +[package.extras] +docs = ["sphinx (>=2.0.0,<3)", "sphinx-argparse (>=0.2.5,<1)", "sphinx-rtd-theme (>=0.4.3,<1)", "towncrier (>=19.9.0rc1)", "proselint (>=0.10.2,<1)"] +testing = ["pytest (>=4.0.0,<6)", "coverage (>=4.5.1,<6)", "pytest-mock (>=2.0.0,<3)", "pytest-env (>=0.6.2,<1)", "packaging (>=20.0)", "xonsh (>=0.9.13,<1)"] + +[[package]] +category = "dev" +description = "Measures number of Terminal column cells of wide-character codes" +name = "wcwidth" +optional = false +python-versions = "*" +version = "0.1.8" + +[[package]] +category = "dev" +description = "Backport of pathlib-compatible object wrapper for zip files" +marker = "python_version < \"3.8\"" +name = "zipp" +optional = false +python-versions = ">=2.7" +version = "1.2.0" + +[package.dependencies] +[package.dependencies.contextlib2] +python = "<3.4" +version = "*" + +[package.extras] +docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] +testing = ["pathlib2", "unittest2", "jaraco.itertools", "func-timeout"] + +[metadata] +content-hash = "ee403d974bc621d06008a783ee5e11bb1cf473bc4488c32c5e6a6e5f3376c59a" +python-versions = "~2.7 || ^3.4" + +[metadata.files] +appdirs = [ + {file = "appdirs-1.4.3-py2.py3-none-any.whl", hash = "sha256:d8b24664561d0d34ddfaec54636d502d7cea6e29c3eaf68f3df6180863e2166e"}, + {file = "appdirs-1.4.3.tar.gz", hash = "sha256:9e5896d1372858f8dd3344faf4e5014d21849c756c8d5701f78f8a103b372d92"}, +] +"aspy.yaml" = [ + {file = "aspy.yaml-1.3.0-py2.py3-none-any.whl", hash = "sha256:463372c043f70160a9ec950c3f1e4c3a82db5fca01d334b6bc89c7164d744bdc"}, + {file = "aspy.yaml-1.3.0.tar.gz", hash = "sha256:e7c742382eff2caed61f87a39d13f99109088e5e93f04d76eb8d4b28aa143f45"}, +] +atomicwrites = [ + {file = "atomicwrites-1.3.0-py2.py3-none-any.whl", hash = "sha256:03472c30eb2c5d1ba9227e4c2ca66ab8287fbfbbda3888aa93dc2e28fc6811b4"}, + {file = "atomicwrites-1.3.0.tar.gz", hash = "sha256:75a9445bac02d8d058d5e1fe689654ba5a6556a1dfd8ce6ec55a0ed79866cfa6"}, +] +attrs = [ + {file = "attrs-19.3.0-py2.py3-none-any.whl", hash = "sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c"}, + {file = "attrs-19.3.0.tar.gz", hash = "sha256:f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72"}, +] +babel = [ + {file = "Babel-2.8.0-py2.py3-none-any.whl", hash = "sha256:d670ea0b10f8b723672d3a6abeb87b565b244da220d76b4dba1b66269ec152d4"}, + {file = "Babel-2.8.0.tar.gz", hash = "sha256:1aac2ae2d0d8ea368fa90906567f5c08463d98ade155c0c4bfedd6a0f7160e38"}, +] +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"}, +] +cleo = [ + {file = "cleo-0.7.6-py2.py3-none-any.whl", hash = "sha256:9443d67e5b2da79b32d820ae41758dd6a25618345cb10b9a022a695e26b291b9"}, + {file = "cleo-0.7.6.tar.gz", hash = "sha256:99cf342406f3499cec43270fcfaf93c126c5164092eca201dfef0f623360b409"}, +] +click = [ + {file = "Click-7.0-py2.py3-none-any.whl", hash = "sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13"}, + {file = "Click-7.0.tar.gz", hash = "sha256:5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7"}, +] +clikit = [ + {file = "clikit-0.4.2-py2.py3-none-any.whl", hash = "sha256:95394982cfa460a77ded2f173380a958e5f90c16972307c19d79b96f6e335326"}, + {file = "clikit-0.4.2.tar.gz", hash = "sha256:f67336462800078e0896cf6ecfa3b460dfea4dfa01de659388a4ff0d83c8d6ca"}, +] +colorama = [ + {file = "colorama-0.4.1-py2.py3-none-any.whl", hash = "sha256:f8ac84de7840f5b9c4e3347b3c1eaa50f7e49c2b07596221daec5edaabbd7c48"}, + {file = "colorama-0.4.1.tar.gz", hash = "sha256:05eed71e2e327246ad6b38c540c4a3117230b19679b875190486ddd2d721422d"}, + {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-4.5.4-cp26-cp26m-macosx_10_12_x86_64.whl", hash = "sha256:eee64c616adeff7db37cc37da4180a3a5b6177f5c46b187894e633f088fb5b28"}, + {file = "coverage-4.5.4-cp27-cp27m-macosx_10_12_x86_64.whl", hash = "sha256:ef824cad1f980d27f26166f86856efe11eff9912c4fed97d3804820d43fa550c"}, + {file = "coverage-4.5.4-cp27-cp27m-macosx_10_13_intel.whl", hash = "sha256:9a334d6c83dfeadae576b4d633a71620d40d1c379129d587faa42ee3e2a85cce"}, + {file = "coverage-4.5.4-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:7494b0b0274c5072bddbfd5b4a6c6f18fbbe1ab1d22a41e99cd2d00c8f96ecfe"}, + {file = "coverage-4.5.4-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:826f32b9547c8091679ff292a82aca9c7b9650f9fda3e2ca6bf2ac905b7ce888"}, + {file = "coverage-4.5.4-cp27-cp27m-win32.whl", hash = "sha256:63a9a5fc43b58735f65ed63d2cf43508f462dc49857da70b8980ad78d41d52fc"}, + {file = "coverage-4.5.4-cp27-cp27m-win_amd64.whl", hash = "sha256:e2ede7c1d45e65e209d6093b762e98e8318ddeff95317d07a27a2140b80cfd24"}, + {file = "coverage-4.5.4-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:dd579709a87092c6dbee09d1b7cfa81831040705ffa12a1b248935274aee0437"}, + {file = "coverage-4.5.4-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:08907593569fe59baca0bf152c43f3863201efb6113ecb38ce7e97ce339805a6"}, + {file = "coverage-4.5.4-cp33-cp33m-macosx_10_10_x86_64.whl", hash = "sha256:6b62544bb68106e3f00b21c8930e83e584fdca005d4fffd29bb39fb3ffa03cb5"}, + {file = "coverage-4.5.4-cp34-cp34m-macosx_10_12_x86_64.whl", hash = "sha256:331cb5115673a20fb131dadd22f5bcaf7677ef758741312bee4937d71a14b2ef"}, + {file = "coverage-4.5.4-cp34-cp34m-manylinux1_i686.whl", hash = "sha256:bf1ef9eb901113a9805287e090452c05547578eaab1b62e4ad456fcc049a9b7e"}, + {file = "coverage-4.5.4-cp34-cp34m-manylinux1_x86_64.whl", hash = "sha256:386e2e4090f0bc5df274e720105c342263423e77ee8826002dcffe0c9533dbca"}, + {file = "coverage-4.5.4-cp34-cp34m-win32.whl", hash = "sha256:fa964bae817babece5aa2e8c1af841bebb6d0b9add8e637548809d040443fee0"}, + {file = "coverage-4.5.4-cp34-cp34m-win_amd64.whl", hash = "sha256:df6712284b2e44a065097846488f66840445eb987eb81b3cc6e4149e7b6982e1"}, + {file = "coverage-4.5.4-cp35-cp35m-macosx_10_12_x86_64.whl", hash = "sha256:efc89291bd5a08855829a3c522df16d856455297cf35ae827a37edac45f466a7"}, + {file = "coverage-4.5.4-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:e4ef9c164eb55123c62411f5936b5c2e521b12356037b6e1c2617cef45523d47"}, + {file = "coverage-4.5.4-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:ff37757e068ae606659c28c3bd0d923f9d29a85de79bf25b2b34b148473b5025"}, + {file = "coverage-4.5.4-cp35-cp35m-win32.whl", hash = "sha256:bf0a7aed7f5521c7ca67febd57db473af4762b9622254291fbcbb8cd0ba5e33e"}, + {file = "coverage-4.5.4-cp35-cp35m-win_amd64.whl", hash = "sha256:19e4df788a0581238e9390c85a7a09af39c7b539b29f25c89209e6c3e371270d"}, + {file = "coverage-4.5.4-cp36-cp36m-macosx_10_13_x86_64.whl", hash = "sha256:60851187677b24c6085248f0a0b9b98d49cba7ecc7ec60ba6b9d2e5574ac1ee9"}, + {file = "coverage-4.5.4-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:245388cda02af78276b479f299bbf3783ef0a6a6273037d7c60dc73b8d8d7755"}, + {file = "coverage-4.5.4-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:c0afd27bc0e307a1ffc04ca5ec010a290e49e3afbe841c5cafc5c5a80ecd81c9"}, + {file = "coverage-4.5.4-cp36-cp36m-win32.whl", hash = "sha256:6ba744056423ef8d450cf627289166da65903885272055fb4b5e113137cfa14f"}, + {file = "coverage-4.5.4-cp36-cp36m-win_amd64.whl", hash = "sha256:af7ed8a8aa6957aac47b4268631fa1df984643f07ef00acd374e456364b373f5"}, + {file = "coverage-4.5.4-cp37-cp37m-macosx_10_13_x86_64.whl", hash = "sha256:3a794ce50daee01c74a494919d5ebdc23d58873747fa0e288318728533a3e1ca"}, + {file = "coverage-4.5.4-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:0be0f1ed45fc0c185cfd4ecc19a1d6532d72f86a2bac9de7e24541febad72650"}, + {file = "coverage-4.5.4-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:eca2b7343524e7ba246cab8ff00cab47a2d6d54ada3b02772e908a45675722e2"}, + {file = "coverage-4.5.4-cp37-cp37m-win32.whl", hash = "sha256:93715dffbcd0678057f947f496484e906bf9509f5c1c38fc9ba3922893cda5f5"}, + {file = "coverage-4.5.4-cp37-cp37m-win_amd64.whl", hash = "sha256:23cc09ed395b03424d1ae30dcc292615c1372bfba7141eb85e11e50efaa6b351"}, + {file = "coverage-4.5.4-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:141f08ed3c4b1847015e2cd62ec06d35e67a3ac185c26f7635f4406b90afa9c5"}, + {file = "coverage-4.5.4.tar.gz", hash = "sha256:e07d9f1a23e9e93ab5c62902833bf3e4b1f65502927379148b6622686223125c"}, + {file = "coverage-5.0.3-cp27-cp27m-macosx_10_12_x86_64.whl", hash = "sha256:cc1109f54a14d940b8512ee9f1c3975c181bbb200306c6d8b87d93376538782f"}, + {file = "coverage-5.0.3-cp27-cp27m-macosx_10_13_intel.whl", hash = "sha256:be18f4ae5a9e46edae3f329de2191747966a34a3d93046dbdf897319923923bc"}, + {file = "coverage-5.0.3-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:3230d1003eec018ad4a472d254991e34241e0bbd513e97a29727c7c2f637bd2a"}, + {file = "coverage-5.0.3-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:e69215621707119c6baf99bda014a45b999d37602cb7043d943c76a59b05bf52"}, + {file = "coverage-5.0.3-cp27-cp27m-win32.whl", hash = "sha256:1daa3eceed220f9fdb80d5ff950dd95112cd27f70d004c7918ca6dfc6c47054c"}, + {file = "coverage-5.0.3-cp27-cp27m-win_amd64.whl", hash = "sha256:51bc7710b13a2ae0c726f69756cf7ffd4362f4ac36546e243136187cfcc8aa73"}, + {file = "coverage-5.0.3-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:9bea19ac2f08672636350f203db89382121c9c2ade85d945953ef3c8cf9d2a68"}, + {file = "coverage-5.0.3-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:5012d3b8d5a500834783689a5d2292fe06ec75dc86ee1ccdad04b6f5bf231691"}, + {file = "coverage-5.0.3-cp35-cp35m-macosx_10_12_x86_64.whl", hash = "sha256:d513cc3db248e566e07a0da99c230aca3556d9b09ed02f420664e2da97eac301"}, + {file = "coverage-5.0.3-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:3dbb72eaeea5763676a1a1efd9b427a048c97c39ed92e13336e726117d0b72bf"}, + {file = "coverage-5.0.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:15cf13a6896048d6d947bf7d222f36e4809ab926894beb748fc9caa14605d9c3"}, + {file = "coverage-5.0.3-cp35-cp35m-win32.whl", hash = "sha256:fca1669d464f0c9831fd10be2eef6b86f5ebd76c724d1e0706ebdff86bb4adf0"}, + {file = "coverage-5.0.3-cp35-cp35m-win_amd64.whl", hash = "sha256:1e44a022500d944d42f94df76727ba3fc0a5c0b672c358b61067abb88caee7a0"}, + {file = "coverage-5.0.3-cp36-cp36m-macosx_10_13_x86_64.whl", hash = "sha256:b26aaf69713e5674efbde4d728fb7124e429c9466aeaf5f4a7e9e699b12c9fe2"}, + {file = "coverage-5.0.3-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:722e4557c8039aad9592c6a4213db75da08c2cd9945320220634f637251c3894"}, + {file = "coverage-5.0.3-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:7afad9835e7a651d3551eab18cbc0fdb888f0a6136169fbef0662d9cdc9987cf"}, + {file = "coverage-5.0.3-cp36-cp36m-win32.whl", hash = "sha256:25dbf1110d70bab68a74b4b9d74f30e99b177cde3388e07cc7272f2168bd1477"}, + {file = "coverage-5.0.3-cp36-cp36m-win_amd64.whl", hash = "sha256:c312e57847db2526bc92b9bfa78266bfbaabac3fdcd751df4d062cd4c23e46dc"}, + {file = "coverage-5.0.3-cp37-cp37m-macosx_10_13_x86_64.whl", hash = "sha256:a8b8ac7876bc3598e43e2603f772d2353d9931709345ad6c1149009fd1bc81b8"}, + {file = "coverage-5.0.3-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:527b4f316e6bf7755082a783726da20671a0cc388b786a64417780b90565b987"}, + {file = "coverage-5.0.3-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:d649dc0bcace6fcdb446ae02b98798a856593b19b637c1b9af8edadf2b150bea"}, + {file = "coverage-5.0.3-cp37-cp37m-win32.whl", hash = "sha256:cd60f507c125ac0ad83f05803063bed27e50fa903b9c2cfee3f8a6867ca600fc"}, + {file = "coverage-5.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:c60097190fe9dc2b329a0eb03393e2e0829156a589bd732e70794c0dd804258e"}, + {file = "coverage-5.0.3-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:d7008a6796095a79544f4da1ee49418901961c97ca9e9d44904205ff7d6aa8cb"}, + {file = "coverage-5.0.3-cp38-cp38-manylinux1_i686.whl", hash = "sha256:ea9525e0fef2de9208250d6c5aeeee0138921057cd67fcef90fbed49c4d62d37"}, + {file = "coverage-5.0.3-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:c62a2143e1313944bf4a5ab34fd3b4be15367a02e9478b0ce800cb510e3bbb9d"}, + {file = "coverage-5.0.3-cp38-cp38m-win32.whl", hash = "sha256:b0840b45187699affd4c6588286d429cd79a99d509fe3de0f209594669bb0954"}, + {file = "coverage-5.0.3-cp38-cp38m-win_amd64.whl", hash = "sha256:76e2057e8ffba5472fd28a3a010431fd9e928885ff480cb278877c6e9943cc2e"}, + {file = "coverage-5.0.3-cp39-cp39m-win32.whl", hash = "sha256:b63dd43f455ba878e5e9f80ba4f748c0a2156dde6e0e6e690310e24d6e8caf40"}, + {file = "coverage-5.0.3-cp39-cp39m-win_amd64.whl", hash = "sha256:da93027835164b8223e8e5af2cf902a4c80ed93cb0909417234f4a9df3bcd9af"}, + {file = "coverage-5.0.3.tar.gz", hash = "sha256:77afca04240c40450c331fa796b3eab6f1e15c5ecf8bf2b8bee9706cd5452fef"}, +] +distlib = [ + {file = "distlib-0.3.0.zip", hash = "sha256:2e166e231a26b36d6dfe35a48c4464346620f8645ed0ace01ee31822b288de21"}, +] +enum34 = [ + {file = "enum34-1.1.9-py2-none-any.whl", hash = "sha256:98df1f1937840b7d8012fea7f0b36392a3e6fd8a2f429c48a3ff4b1aad907f3f"}, + {file = "enum34-1.1.9-py3-none-any.whl", hash = "sha256:708aabfb3d5898f99674c390d360d59efdd08547019763622365f19e84a7fef4"}, + {file = "enum34-1.1.9.tar.gz", hash = "sha256:13ef9a1c478203252107f66c25b99b45b1865693ca1284aab40dafa7e1e7ac17"}, +] +filelock = [ + {file = "filelock-3.0.12-py3-none-any.whl", hash = "sha256:929b7d63ec5b7d6b71b0fa5ac14e030b3f70b75747cef1b10da9b879fef15836"}, + {file = "filelock-3.0.12.tar.gz", hash = "sha256:18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59"}, +] +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.3.0-py2-none-any.whl", hash = "sha256:49b3f5b064b6e3afc3316421a3f25f66c137ae88f068abbf72830170033c5e16"}, + {file = "futures-3.3.0.tar.gz", hash = "sha256:7e033af76a5e35f58e56da7a91e687706faf4e7bdfb2cbc3f2cca6b9bcda9794"}, +] +identify = [ + {file = "identify-1.4.11-py2.py3-none-any.whl", hash = "sha256:1222b648251bdcb8deb240b294f450fbf704c7984e08baa92507e4ea10b436d5"}, + {file = "identify-1.4.11.tar.gz", hash = "sha256:d824ebe21f38325c771c41b08a95a761db1982f1fc0eee37c6c97df3f1636b96"}, +] +importlib-metadata = [ + {file = "importlib_metadata-1.1.3-py2.py3-none-any.whl", hash = "sha256:7c7f8ac40673f507f349bef2eed21a0e5f01ddf5b2a7356a6c65eb2099b53764"}, + {file = "importlib_metadata-1.1.3.tar.gz", hash = "sha256:7a99fb4084ffe6dae374961ba7a6521b79c1d07c658ab3a28aa264ee1d1b14e3"}, + {file = "importlib_metadata-1.5.0-py2.py3-none-any.whl", hash = "sha256:b97607a1a18a5100839aec1dc26a1ea17ee0d93b20b0f008d80a5a050afb200b"}, + {file = "importlib_metadata-1.5.0.tar.gz", hash = "sha256:06f5b3a99029c7134207dd882428a66992a9de2bef7c2b699b5641f9886c3302"}, +] +importlib-resources = [ + {file = "importlib_resources-1.0.2-py2.py3-none-any.whl", hash = "sha256:6e2783b2538bd5a14678284a3962b0660c715e5a0f10243fd5e00a4b5974f50b"}, + {file = "importlib_resources-1.0.2.tar.gz", hash = "sha256:d3279fd0f6f847cced9f7acc19bd3e5df54d34f93a2e7bb5f238f81545787078"}, + {file = "importlib_resources-1.2.0-py2.py3-none-any.whl", hash = "sha256:6aefffdf63634bd94600dd0691fd6430902272bace0572934fa23c81473a7ff7"}, + {file = "importlib_resources-1.2.0.tar.gz", hash = "sha256:ce84b1c9c05078e1797700505bd1bc386cee9561002a9bdcfcba634adc6333e5"}, +] +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-7.2.0.tar.gz", hash = "sha256:409cd48d4db7052af495b09dec721011634af3753ae1ef92d2b32f73a745f832"}, + {file = "more_itertools-7.2.0-py3-none-any.whl", hash = "sha256:92b8c4b06dac4f0611c0729b2f2ede52b2e1bac1ab48f089c7ddc12e26bb60c4"}, + {file = "more-itertools-8.2.0.tar.gz", hash = "sha256:b1ddb932186d8a6ac451e1d95844b382f55e12686d51ca0c68b6f61f2ab7a507"}, + {file = "more_itertools-8.2.0-py3-none-any.whl", hash = "sha256:5dd8bcf33e5f9513ffa06d5ad33d78f31e1931ac9a18f33d37e77a180d393a7c"}, +] +nodeenv = [ + {file = "nodeenv-1.3.5-py2.py3-none-any.whl", hash = "sha256:5b2438f2e42af54ca968dd1b374d14a1194848955187b0e5e4be1f73813a5212"}, +] +packaging = [ + {file = "packaging-20.1-py2.py3-none-any.whl", hash = "sha256:170748228214b70b672c581a3dd610ee51f733018650740e98c7df862a583f73"}, + {file = "packaging-20.1.tar.gz", hash = "sha256:e665345f9eef0c621aa0bf2f8d78cf6d21904eef16a93f020240b704a57f1334"}, +] +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.7.0-py2.py3-none-any.whl", hash = "sha256:163b0632d4e31cef212976cf57b43d9fd6b0bac6e67c26015d611a647d5e7424"}, + {file = "pathspec-0.7.0.tar.gz", hash = "sha256:562aa70af2e0d434367d9790ad37aed893de47f1693e4201fd1d3dca15d19b96"}, +] +pluggy = [ + {file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"}, + {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"}, +] +pre-commit = [ + {file = "pre_commit-1.18.3-py2.py3-none-any.whl", hash = "sha256:fa78ff96e8e9ac94c748388597693f18b041a181c94a4f039ad20f45287ba44a"}, + {file = "pre_commit-1.18.3.tar.gz", hash = "sha256:1d3c0587bda7c4e537a46c27f2c84aa006acc18facf9970bf947df596ce91f3f"}, + {file = "pre_commit-1.21.0-py2.py3-none-any.whl", hash = "sha256:f92a359477f3252452ae2e8d3029de77aec59415c16ae4189bcfba40b757e029"}, + {file = "pre_commit-1.21.0.tar.gz", hash = "sha256:8f48d8637bdae6fa70cc97db9c1dd5aa7c5c8bf71968932a380628c25978b850"}, +] +py = [ + {file = "py-1.8.1-py2.py3-none-any.whl", hash = "sha256:c20fdd83a5dbc0af9efd622bee9a5564e278f6380fffcacc43ba6f43db2813b0"}, + {file = "py-1.8.1.tar.gz", hash = "sha256:5e27081401262157467ad6e7f851b7aa402c5852dbcb3dae06768434de5752aa"}, +] +pylev = [ + {file = "pylev-1.3.0-py2.py3-none-any.whl", hash = "sha256:1d29a87beb45ebe1e821e7a3b10da2b6b2f4c79b43f482c2df1a1f748a6e114e"}, + {file = "pylev-1.3.0.tar.gz", hash = "sha256:063910098161199b81e453025653ec53556c1be7165a9b7c50be2f4d57eae1c3"}, +] +pyparsing = [ + {file = "pyparsing-2.4.6-py2.py3-none-any.whl", hash = "sha256:c342dccb5250c08d45fd6f8b4a559613ca603b57498511740e65cd11a2e7dcec"}, + {file = "pyparsing-2.4.6.tar.gz", hash = "sha256:4c830582a84fb022400b85429791bc551f1f4871c33f23e44f353119e92f969f"}, +] +pytest = [ + {file = "pytest-4.6.9-py2.py3-none-any.whl", hash = "sha256:c77a5f30a90e0ce24db9eaa14ddfd38d4afb5ea159309bdd2dae55b931bc9324"}, + {file = "pytest-4.6.9.tar.gz", hash = "sha256:19e8f75eac01dd3f211edd465b39efbcbdc8fc5f7866d7dd49fedb30d8adf339"}, +] +pytest-cov = [ + {file = "pytest-cov-2.8.1.tar.gz", hash = "sha256:cc6742d8bac45070217169f5f72ceee1e0e55b0221f54bcf24845972d3a47f2b"}, + {file = "pytest_cov-2.8.1-py2.py3-none-any.whl", hash = "sha256:cdbdef4f870408ebdbfeb44e63e07eb18bb4619fae852f6e760645fa36172626"}, +] +python-dateutil = [ + {file = "python-dateutil-2.8.1.tar.gz", hash = "sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c"}, + {file = "python_dateutil-2.8.1-py2.py3-none-any.whl", hash = "sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a"}, +] +pytz = [ + {file = "pytz-2019.3-py2.py3-none-any.whl", hash = "sha256:1c557d7d0e871de1f5ccd5833f60fb2550652da6be2693c1e02300743d21500d"}, + {file = "pytz-2019.3.tar.gz", hash = "sha256:b02c06db6cf09c12dd25137e563b31700d3b80fcc4ad23abb7a315f2789819be"}, +] +pytzdata = [ + {file = "pytzdata-2019.3-py2.py3-none-any.whl", hash = "sha256:84c52b9a47d097fcd483f047a544979de6c3a86e94c845e3569e9f8acd0fa071"}, + {file = "pytzdata-2019.3.tar.gz", hash = "sha256:fac06f7cdfa903188dc4848c655e4adaee67ee0f2fe08e7daf815cf2a761ee5e"}, +] +pyyaml = [ + {file = "PyYAML-5.2-cp27-cp27m-win32.whl", hash = "sha256:35ace9b4147848cafac3db142795ee42deebe9d0dad885ce643928e88daebdcc"}, + {file = "PyYAML-5.2-cp27-cp27m-win_amd64.whl", hash = "sha256:ebc4ed52dcc93eeebeae5cf5deb2ae4347b3a81c3fa12b0b8c976544829396a4"}, + {file = "PyYAML-5.2-cp35-cp35m-win32.whl", hash = "sha256:38a4f0d114101c58c0f3a88aeaa44d63efd588845c5a2df5290b73db8f246d15"}, + {file = "PyYAML-5.2-cp35-cp35m-win_amd64.whl", hash = "sha256:483eb6a33b671408c8529106df3707270bfacb2447bf8ad856a4b4f57f6e3075"}, + {file = "PyYAML-5.2-cp36-cp36m-win32.whl", hash = "sha256:7f38e35c00e160db592091751d385cd7b3046d6d51f578b29943225178257b31"}, + {file = "PyYAML-5.2-cp36-cp36m-win_amd64.whl", hash = "sha256:0e7f69397d53155e55d10ff68fdfb2cf630a35e6daf65cf0bdeaf04f127c09dc"}, + {file = "PyYAML-5.2-cp37-cp37m-win32.whl", hash = "sha256:e4c015484ff0ff197564917b4b4246ca03f411b9bd7f16e02a2f586eb48b6d04"}, + {file = "PyYAML-5.2-cp37-cp37m-win_amd64.whl", hash = "sha256:4b6be5edb9f6bb73680f5bf4ee08ff25416d1400fbd4535fe0069b2994da07cd"}, + {file = "PyYAML-5.2-cp38-cp38-win32.whl", hash = "sha256:8100c896ecb361794d8bfdb9c11fce618c7cf83d624d73d5ab38aef3bc82d43f"}, + {file = "PyYAML-5.2-cp38-cp38-win_amd64.whl", hash = "sha256:2e9f0b7c5914367b0916c3c104a024bb68f269a486b9d04a2e8ac6f6597b7803"}, + {file = "PyYAML-5.2.tar.gz", hash = "sha256:c0ee8eca2c582d29c3c2ec6e2c4f703d1b7f1fb10bc72317355a746057e7346c"}, + {file = "PyYAML-5.3-cp27-cp27m-win32.whl", hash = "sha256:940532b111b1952befd7db542c370887a8611660d2b9becff75d39355303d82d"}, + {file = "PyYAML-5.3-cp27-cp27m-win_amd64.whl", hash = "sha256:059b2ee3194d718896c0ad077dd8c043e5e909d9180f387ce42012662a4946d6"}, + {file = "PyYAML-5.3-cp35-cp35m-win32.whl", hash = "sha256:4fee71aa5bc6ed9d5f116327c04273e25ae31a3020386916905767ec4fc5317e"}, + {file = "PyYAML-5.3-cp35-cp35m-win_amd64.whl", hash = "sha256:dbbb2379c19ed6042e8f11f2a2c66d39cceb8aeace421bfc29d085d93eda3689"}, + {file = "PyYAML-5.3-cp36-cp36m-win32.whl", hash = "sha256:e3a057b7a64f1222b56e47bcff5e4b94c4f61faac04c7c4ecb1985e18caa3994"}, + {file = "PyYAML-5.3-cp36-cp36m-win_amd64.whl", hash = "sha256:74782fbd4d4f87ff04159e986886931456a1894c61229be9eaf4de6f6e44b99e"}, + {file = "PyYAML-5.3-cp37-cp37m-win32.whl", hash = "sha256:24521fa2890642614558b492b473bee0ac1f8057a7263156b02e8b14c88ce6f5"}, + {file = "PyYAML-5.3-cp37-cp37m-win_amd64.whl", hash = "sha256:1cf708e2ac57f3aabc87405f04b86354f66799c8e62c28c5fc5f88b5521b2dbf"}, + {file = "PyYAML-5.3-cp38-cp38-win32.whl", hash = "sha256:70024e02197337533eef7b85b068212420f950319cc8c580261963aefc75f811"}, + {file = "PyYAML-5.3-cp38-cp38-win_amd64.whl", hash = "sha256:cb1f2f5e426dc9f07a7681419fe39cee823bb74f723f36f70399123f439e9b20"}, + {file = "PyYAML-5.3.tar.gz", hash = "sha256:e9f45bd5b92c7974e59bcd2dcc8631a6b6cc380a904725fce7bc08872e691615"}, +] +regex = [ + {file = "regex-2020.2.20-cp27-cp27m-win32.whl", hash = "sha256:99272d6b6a68c7ae4391908fc15f6b8c9a6c345a46b632d7fdb7ef6c883a2bbb"}, + {file = "regex-2020.2.20-cp27-cp27m-win_amd64.whl", hash = "sha256:974535648f31c2b712a6b2595969f8ab370834080e00ab24e5dbb9d19b8bfb74"}, + {file = "regex-2020.2.20-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:5de40649d4f88a15c9489ed37f88f053c15400257eeb18425ac7ed0a4e119400"}, + {file = "regex-2020.2.20-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:82469a0c1330a4beb3d42568f82dffa32226ced006e0b063719468dcd40ffdf0"}, + {file = "regex-2020.2.20-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:d58a4fa7910102500722defbde6e2816b0372a4fcc85c7e239323767c74f5cbc"}, + {file = "regex-2020.2.20-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:f1ac2dc65105a53c1c2d72b1d3e98c2464a133b4067a51a3d2477b28449709a0"}, + {file = "regex-2020.2.20-cp36-cp36m-win32.whl", hash = "sha256:8c2b7fa4d72781577ac45ab658da44c7518e6d96e2a50d04ecb0fd8f28b21d69"}, + {file = "regex-2020.2.20-cp36-cp36m-win_amd64.whl", hash = "sha256:269f0c5ff23639316b29f31df199f401e4cb87529eafff0c76828071635d417b"}, + {file = "regex-2020.2.20-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:bed7986547ce54d230fd8721aba6fd19459cdc6d315497b98686d0416efaff4e"}, + {file = "regex-2020.2.20-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:046e83a8b160aff37e7034139a336b660b01dbfe58706f9d73f5cdc6b3460242"}, + {file = "regex-2020.2.20-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:b33ebcd0222c1d77e61dbcd04a9fd139359bded86803063d3d2d197b796c63ce"}, + {file = "regex-2020.2.20-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:bba52d72e16a554d1894a0cc74041da50eea99a8483e591a9edf1025a66843ab"}, + {file = "regex-2020.2.20-cp37-cp37m-win32.whl", hash = "sha256:01b2d70cbaed11f72e57c1cfbaca71b02e3b98f739ce33f5f26f71859ad90431"}, + {file = "regex-2020.2.20-cp37-cp37m-win_amd64.whl", hash = "sha256:113309e819634f499d0006f6200700c8209a2a8bf6bd1bdc863a4d9d6776a5d1"}, + {file = "regex-2020.2.20-cp38-cp38-manylinux1_i686.whl", hash = "sha256:25f4ce26b68425b80a233ce7b6218743c71cf7297dbe02feab1d711a2bf90045"}, + {file = "regex-2020.2.20-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:9b64a4cc825ec4df262050c17e18f60252cdd94742b4ba1286bcfe481f1c0f26"}, + {file = "regex-2020.2.20-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:9ff16d994309b26a1cdf666a6309c1ef51ad4f72f99d3392bcd7b7139577a1f2"}, + {file = "regex-2020.2.20-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:c7f58a0e0e13fb44623b65b01052dae8e820ed9b8b654bb6296bc9c41f571b70"}, + {file = "regex-2020.2.20-cp38-cp38-win32.whl", hash = "sha256:200539b5124bc4721247a823a47d116a7a23e62cc6695744e3eb5454a8888e6d"}, + {file = "regex-2020.2.20-cp38-cp38-win_amd64.whl", hash = "sha256:7f78f963e62a61e294adb6ff5db901b629ef78cb2a1cfce3cf4eeba80c1c67aa"}, + {file = "regex-2020.2.20.tar.gz", hash = "sha256:9e9624440d754733eddbcd4614378c18713d2d9d0dc647cf9c72f64e39671be5"}, +] +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.14.0-py2.py3-none-any.whl", hash = "sha256:8f3cd2e254d8f793e7f3d6d9df77b92252b52637291d0f0da013c76ea2724b6c"}, + {file = "six-1.14.0.tar.gz", hash = "sha256:236bdbdce46e6e6a3d61a337c0f8b763ca1e8717c03b369e87a7ec7ce1319c0a"}, +] +toml = [ + {file = "toml-0.10.0-py2.7.egg", hash = "sha256:f1db651f9657708513243e61e6cc67d101a39bad662eaa9b5546f789338e07a3"}, + {file = "toml-0.10.0-py2.py3-none-any.whl", hash = "sha256:235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e"}, + {file = "toml-0.10.0.tar.gz", hash = "sha256:229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c"}, +] +tox = [ + {file = "tox-3.12.1-py2.py3-none-any.whl", hash = "sha256:f5c8e446b51edd2ea97df31d4ded8c8b72e7d6c619519da6bb6084b9dd5770f9"}, + {file = "tox-3.12.1.tar.gz", hash = "sha256:f87fd33892a2df0950e5e034def9468988b8d008c7e9416be665fcc0dd45b14f"}, + {file = "tox-3.14.5-py2.py3-none-any.whl", hash = "sha256:0cbe98369081fa16bd6f1163d3d0b2a62afa29d402ccfad2bd09fb2668be0956"}, + {file = "tox-3.14.5.tar.gz", hash = "sha256:676f1e3e7de245ad870f956436b84ea226210587d1f72c8dfb8cd5ac7b6f0e70"}, +] +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"}, + {file = "typed_ast-1.4.1-cp35-cp35m-win32.whl", hash = "sha256:0c2c07682d61a629b68433afb159376e24e5b2fd4641d35424e462169c0a7919"}, + {file = "typed_ast-1.4.1-cp35-cp35m-win_amd64.whl", hash = "sha256:4083861b0aa07990b619bd7ddc365eb7fa4b817e99cf5f8d9cf21a42780f6e01"}, + {file = "typed_ast-1.4.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:269151951236b0f9a6f04015a9004084a5ab0d5f19b57de779f908621e7d8b75"}, + {file = "typed_ast-1.4.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:24995c843eb0ad11a4527b026b4dde3da70e1f2d8806c99b7b4a7cf491612652"}, + {file = "typed_ast-1.4.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:fe460b922ec15dd205595c9b5b99e2f056fd98ae8f9f56b888e7a17dc2b757e7"}, + {file = "typed_ast-1.4.1-cp36-cp36m-win32.whl", hash = "sha256:4e3e5da80ccbebfff202a67bf900d081906c358ccc3d5e3c8aea42fdfdfd51c1"}, + {file = "typed_ast-1.4.1-cp36-cp36m-win_amd64.whl", hash = "sha256:249862707802d40f7f29f6e1aad8d84b5aa9e44552d2cc17384b209f091276aa"}, + {file = "typed_ast-1.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8ce678dbaf790dbdb3eba24056d5364fb45944f33553dd5869b7580cdbb83614"}, + {file = "typed_ast-1.4.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:c9e348e02e4d2b4a8b2eedb48210430658df6951fa484e59de33ff773fbd4b41"}, + {file = "typed_ast-1.4.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:bcd3b13b56ea479b3650b82cabd6b5343a625b0ced5429e4ccad28a8973f301b"}, + {file = "typed_ast-1.4.1-cp37-cp37m-win32.whl", hash = "sha256:d5d33e9e7af3b34a40dc05f498939f0ebf187f07c385fd58d591c533ad8562fe"}, + {file = "typed_ast-1.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:0666aa36131496aed8f7be0410ff974562ab7eeac11ef351def9ea6fa28f6355"}, + {file = "typed_ast-1.4.1-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:d205b1b46085271b4e15f670058ce182bd1199e56b317bf2ec004b6a44f911f6"}, + {file = "typed_ast-1.4.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:6daac9731f172c2a22ade6ed0c00197ee7cc1221aa84cfdf9c31defeb059a907"}, + {file = "typed_ast-1.4.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:498b0f36cc7054c1fead3d7fc59d2150f4d5c6c56ba7fb150c013fbc683a8d2d"}, + {file = "typed_ast-1.4.1-cp38-cp38-win32.whl", hash = "sha256:715ff2f2df46121071622063fc7543d9b1fd19ebfc4f5c8895af64a77a8c852c"}, + {file = "typed_ast-1.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:fc0fea399acb12edbf8a628ba8d2312f583bdbdb3335635db062fa98cf71fca4"}, + {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.1-py2-none-any.whl", hash = "sha256:910f4656f54de5993ad9304959ce9bb903f90aadc7c67a0bef07e678014e892d"}, + {file = "typing_extensions-3.7.4.1-py3-none-any.whl", hash = "sha256:cf8b63fedea4d89bab840ecbb93e75578af28f76f66c35889bd7065f5af88575"}, + {file = "typing_extensions-3.7.4.1.tar.gz", hash = "sha256:091ecc894d5e908ac75209f10d5b4f118fbdb2eb1ede6a63544054bb1edb41f2"}, +] +virtualenv = [ + {file = "virtualenv-20.0.8-py2.py3-none-any.whl", hash = "sha256:0c04c7e8e0314470b4c2b43740ff68be1c62bb3fdef8309341ff1daea60d49d1"}, + {file = "virtualenv-20.0.8.tar.gz", hash = "sha256:1f0369d068d9761b5c1ed7b44dad1ec124727eb10bc7f4aaefbba0cdca3bd924"}, +] +wcwidth = [ + {file = "wcwidth-0.1.8-py2.py3-none-any.whl", hash = "sha256:8fd29383f539be45b20bd4df0dc29c20ba48654a41e661925e612311e9f3c603"}, + {file = "wcwidth-0.1.8.tar.gz", hash = "sha256:f28b3e8a6483e5d49e7f8949ac1a78314e740333ae305b4ba5defd3e74fb37a8"}, +] +zipp = [ + {file = "zipp-1.2.0-py2.py3-none-any.whl", hash = "sha256:e0d9e63797e483a30d27e09fffd308c59a700d365ec34e93cc100844168bf921"}, + {file = "zipp-1.2.0.tar.gz", hash = "sha256:c70410551488251b0fee67b460fb9a536af8d6f9f008ad10ac51f615b6a521b1"}, +] diff --git a/pyproject.toml b/pyproject.toml index 34993096..c645f98e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,7 +33,7 @@ pytz = ">=2018.3" babel = "^2.5" cleo = "^0.7.5" tox = "^3.0" -black = { version = "^19.3b0", python = "^3.6" } +black = { version = "^19.3b0", markers = "python_version >= '3.6' and python_version < '4.0' and implementation_name != 'pypy'" } pre-commit = "^1.10" From cbcebfb1ce4b9dbd2fa105b256188036bf4dcd2b Mon Sep 17 00:00:00 2001 From: Grzegorz Redlicki Date: Fri, 6 Mar 2020 10:28:46 +0100 Subject: [PATCH 051/256] `is_birthday` should be `is_anniversary` (#298) * Prepare an alias for is_birthday to have more universal method * Change the basic method for checking anniversary | tests for birthday * Prepare separated tests for is_birthday (backward compatibility) --- pendulum/date.py | 11 ++++++++--- pendulum/datetime.py | 9 +++++++-- tests/date/test_comparison.py | 30 +++++++++++++++++++++++------- tests/datetime/test_comparison.py | 31 ++++++++++++++++++++++++------- 4 files changed, 62 insertions(+), 19 deletions(-) diff --git a/pendulum/date.py b/pendulum/date.py index 2d88bc87..6e505273 100644 --- a/pendulum/date.py +++ b/pendulum/date.py @@ -196,9 +196,9 @@ def is_same_day(self, dt): """ return self == dt - def is_birthday(self, dt=None): + def is_anniversary(self, dt=None): """ - Check if its the birthday. + Check if its the anniversary. Compares the date/month values of the two dates. @@ -207,10 +207,15 @@ def is_birthday(self, dt=None): if dt is None: dt = Date.today() - instance = dt1 = self.__class__(dt.year, dt.month, dt.day) + instance = self.__class__(dt.year, dt.month, dt.day) 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): diff --git a/pendulum/datetime.py b/pendulum/datetime.py index b9c3e09b..b7d9fd1f 100644 --- a/pendulum/datetime.py +++ b/pendulum/datetime.py @@ -571,9 +571,9 @@ def is_same_day(self, dt): return self.to_date_string() == dt.to_date_string() - def is_birthday(self, dt=None): + def is_anniversary(self, dt=None): """ - Check if its the birthday. + Check if its the anniversary. Compares the date/month values of the two dates. :rtype: bool @@ -585,6 +585,11 @@ def is_birthday(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( diff --git a/tests/date/test_comparison.py b/tests/date/test_comparison.py index e5a05035..75b0b356 100644 --- a/tests/date/test_comparison.py +++ b/tests/date/test_comparison.py @@ -137,14 +137,30 @@ def test_less_than_or_equal_false(): assert not d1 <= d3 -def test_is_birthday(): +def test_is_anniversary(): d = pendulum.Date.today() - a_birthday = d.subtract(years=1) - assert a_birthday.is_birthday() - not_a_birthday = d.subtract(days=1) - assert not not_a_birthday.is_birthday() - also_not_a_birthday = d.add(days=2) - assert not also_not_a_birthday.is_birthday() + an_anniversary = d.subtract(years=1) + assert an_anniversary.is_anniversary() + not_an_anniversary = d.subtract(days=1) + assert not not_an_anniversary.is_anniversary() + also_not_an_anniversary = d.add(days=2) + assert not also_not_an_anniversary.is_anniversary() + + d1 = pendulum.Date(1987, 4, 23) + d2 = pendulum.Date(2014, 9, 26) + d3 = pendulum.Date(2014, 4, 23) + assert not d2.is_anniversary(d1) + assert d3.is_anniversary(d1) + + +def test_is_birthday(): # backward compatibility + d = pendulum.Date.today() + an_anniversary = d.subtract(years=1) + assert an_anniversary.is_birthday() + not_an_anniversary = d.subtract(days=1) + assert not not_an_anniversary.is_birthday() + also_not_an_anniversary = d.add(days=2) + assert not also_not_an_anniversary.is_birthday() d1 = pendulum.Date(1987, 4, 23) d2 = pendulum.Date(2014, 9, 26) diff --git a/tests/datetime/test_comparison.py b/tests/datetime/test_comparison.py index e556f0d0..02d4d42c 100644 --- a/tests/datetime/test_comparison.py +++ b/tests/datetime/test_comparison.py @@ -237,15 +237,32 @@ def test_less_than_or_equal_with_timezone_false(): assert not d1 <= d3 -def test_is_birthday(): +def test_is_anniversary(): with pendulum.test(pendulum.now()): d = pendulum.now() - a_birthday = d.subtract(years=1) - assert a_birthday.is_birthday() - not_a_birthday = d.subtract(days=1) - assert not not_a_birthday.is_birthday() - also_not_a_birthday = d.add(days=2) - assert not also_not_a_birthday.is_birthday() + an_anniversary = d.subtract(years=1) + assert an_anniversary.is_anniversary() + not_an_anniversary = d.subtract(days=1) + assert not not_an_anniversary.is_anniversary() + also_not_an_anniversary = d.add(days=2) + assert not also_not_an_anniversary.is_anniversary() + + d1 = pendulum.datetime(1987, 4, 23) + d2 = pendulum.datetime(2014, 9, 26) + d3 = pendulum.datetime(2014, 4, 23) + assert not d2.is_anniversary(d1) + assert d3.is_anniversary(d1) + + +def test_is_birthday(): # backward compatibility + with pendulum.test(pendulum.now()): + d = pendulum.now() + an_anniversary = d.subtract(years=1) + assert an_anniversary.is_birthday() + not_an_anniversary = d.subtract(days=1) + assert not not_an_anniversary.is_birthday() + also_not_an_anniversary = d.add(days=2) + assert not also_not_an_anniversary.is_birthday() d1 = pendulum.datetime(1987, 4, 23) d2 = pendulum.datetime(2014, 9, 26) From 58b10e3991ebd8e8e51c1181025c11285d5f180a Mon Sep 17 00:00:00 2001 From: Grzegorz Redlicki Date: Fri, 6 Mar 2020 10:30:52 +0100 Subject: [PATCH 052/256] Add an additional token for the year's string formatting (#299) --- docs/docs/string_formatting.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/docs/string_formatting.md b/docs/docs/string_formatting.md index 27c7a20c..11aab5d2 100644 --- a/docs/docs/string_formatting.md +++ b/docs/docs/string_formatting.md @@ -108,6 +108,7 @@ The following tokens are currently supported: | ------------------------------ | ------------- | ------------------------------------------ | | **Year** | YYYY | 2000, 2001, 2002 ... 2012, 2013 | | | YY | 00, 01, 02 ... 12, 13 | +| | Y | 2000, 2001, 2002 ... 2012, 2013 | | **Quarter** | Q | 1 2 3 4 | | | Qo | 1st 2nd 3rd 4th | | **Month** | MMMM | January, February, March ... | From d51e8f23c0206d10c104444e81dbbea058dcdecc Mon Sep 17 00:00:00 2001 From: Joshua Carp Date: Fri, 6 Mar 2020 05:01:01 -0500 Subject: [PATCH 053/256] Rename mixing to mixin. (#317) --- pendulum/date.py | 4 ++-- pendulum/mixins/default.py | 2 +- pendulum/time.py | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pendulum/date.py b/pendulum/date.py index 6e505273..7f2f1fdb 100644 --- a/pendulum/date.py +++ b/pendulum/date.py @@ -9,7 +9,7 @@ from .helpers import add_duration from .period import Period -from .mixins.default import FormattableMixing +from .mixins.default import FormattableMixin from .constants import ( DAYS_PER_WEEK, YEARS_PER_DECADE, @@ -26,7 +26,7 @@ from .exceptions import PendulumException -class Date(FormattableMixing, date): +class Date(FormattableMixin, date): # Names of days of the week _days = { diff --git a/pendulum/mixins/default.py b/pendulum/mixins/default.py index b1b5a178..e2ad8135 100644 --- a/pendulum/mixins/default.py +++ b/pendulum/mixins/default.py @@ -3,7 +3,7 @@ _formatter = Formatter() -class FormattableMixing(object): +class FormattableMixin(object): _formatter = _formatter diff --git a/pendulum/time.py b/pendulum/time.py index eb449b12..73664b64 100644 --- a/pendulum/time.py +++ b/pendulum/time.py @@ -5,11 +5,11 @@ from datetime import time, timedelta from .duration import Duration, AbsoluteDuration -from .mixins.default import FormattableMixing +from .mixins.default import FormattableMixin from .constants import USECS_PER_SEC, SECS_PER_HOUR, SECS_PER_MIN -class Time(FormattableMixing, time): +class Time(FormattableMixin, time): """ Represents a time instance as hour, minute, second, microsecond. """ From c2ca5f8c95c9c6d7f65eeccb098087886e12f1ee Mon Sep 17 00:00:00 2001 From: Joshua Carp Date: Fri, 6 Mar 2020 05:02:17 -0500 Subject: [PATCH 054/256] Drop deprecated `formatter` parameter from docs. (#316) --- docs/docs/string_formatting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/string_formatting.md b/docs/docs/string_formatting.md index 11aab5d2..ebaeae6c 100644 --- a/docs/docs/string_formatting.md +++ b/docs/docs/string_formatting.md @@ -170,6 +170,6 @@ To escape characters in format strings, you can wrap the characters in square br >>> import pendulum >>> dt = pendulum.now() ->>> dt.format('[today] dddd', formatter='alternative') +>>> dt.format('[today] dddd') 'today Sunday' ``` From bffedf6345c62289828dd835f848f6b9ea2ef85b Mon Sep 17 00:00:00 2001 From: Andrew Schultz Date: Fri, 6 Mar 2020 04:07:48 -0600 Subject: [PATCH 055/256] small typo/word choice/punctuation fixes on reading entire document (#368) --- docs/docs/addition_subtraction.md | 2 +- docs/docs/fluent_helpers.md | 8 ++++---- docs/docs/instantiation.md | 4 ++-- docs/docs/introduction.md | 2 +- docs/docs/limitations.md | 2 +- docs/docs/localization.md | 2 +- docs/docs/modifiers.md | 2 +- docs/docs/parsing.md | 4 ++-- docs/docs/period.md | 6 +++--- docs/docs/string_formatting.md | 2 +- docs/docs/testing.md | 2 +- docs/docs/timezones.md | 10 +++++----- 12 files changed, 23 insertions(+), 23 deletions(-) diff --git a/docs/docs/addition_subtraction.md b/docs/docs/addition_subtraction.md index 87626a13..686f67f0 100644 --- a/docs/docs/addition_subtraction.md +++ b/docs/docs/addition_subtraction.md @@ -1,6 +1,6 @@ # Addition and Subtraction -To easily adding and subtracting time, you can use the `add()` and `subtract()` +To easily add and subtract time, you can use the `add()` and `subtract()` methods. Each method returns a new `DateTime` instance. diff --git a/docs/docs/fluent_helpers.md b/docs/docs/fluent_helpers.md index 5b020e59..2ace4910 100644 --- a/docs/docs/fluent_helpers.md +++ b/docs/docs/fluent_helpers.md @@ -1,8 +1,8 @@ # Fluent helpers -Pendulum provides helpers that returns a new instance with some attributes +Pendulum provides helpers that return a new instance with some attributes modified compared to the original instance. -However, none of these helpers, with the exception of explicitely setting the +However, none of these helpers, with the exception of explicitly setting the timezone, will change the timezone of the instance. Specifically, setting the timestamp will not set the corresponding timezone to UTC. @@ -38,8 +38,8 @@ You can also modify the timezone. >>> dt.set(tz='Europe/London') ``` -Setting the timezone just modify the timezone information without -making any conversion while `in_timezone()` (or `in_tz()`) +Setting the timezone just modifies the timezone information without +making any conversion, while `in_timezone()` (or `in_tz()`) converts the time in the appropriate timezone. ```python diff --git a/docs/docs/instantiation.md b/docs/docs/instantiation.md index 43fa7a4d..5710a1db 100644 --- a/docs/docs/instantiation.md +++ b/docs/docs/instantiation.md @@ -16,7 +16,7 @@ True `datetime()` sets the time to `00:00:00` if it's not specified, and the timezone (the `tz` keyword argument) to `UTC`. -It otherwise can be a `Timezone` instance or simply a string timezone value. +Otherwise it can be a `Timezone` instance or simply a string timezone value. ```python >>> import pendulum @@ -88,7 +88,7 @@ and each has their time value set to `00:00:00`. ``` Pendulum enforces timezone aware datetimes, and using them is the preferred and recommended way -of using the library, however is you really need a **naive** `DateTime` object, the `naive()` helper +of using the library. However, if you really need a **naive** `DateTime` object, the `naive()` helper is there for you. ```python diff --git a/docs/docs/introduction.md b/docs/docs/introduction.md index 95042125..fbbab976 100644 --- a/docs/docs/introduction.md +++ b/docs/docs/introduction.md @@ -6,7 +6,7 @@ It provides classes that are drop-in replacements for the native ones (they inhe Special care has been taken to ensure timezones are handled correctly, and are based on the underlying `tzinfo` implementation. -For example all comparisons are done in `UTC` or in the timezone of the datetime being used. +For example, all comparisons are done in `UTC` or in the timezone of the datetime being used. ```python >>> import pendulum diff --git a/docs/docs/limitations.md b/docs/docs/limitations.md index 4470da8d..7deff230 100644 --- a/docs/docs/limitations.md +++ b/docs/docs/limitations.md @@ -24,7 +24,7 @@ Here is a list (non-exhaustive) of the reported cases with a possible solution, pymysql.converters.conversions[pendulum.DateTime] = pymysql.converters.escape_datetime ``` -* `django` will use the `isoformat()` method to store datetimes in the database. However since `pendulum` is always timezone aware the offset information will always be returned by `isoformat()` raising an error, at least for MySQL databases. To work around it you can either create your own `DateTimeField` or use the previous workaround for `MySQLdb`: +* `django` will use the `isoformat()` method to store datetimes in the database. However, since `pendulum` is always timezone aware, the offset information will always be returned by `isoformat()` raising an error, at least for MySQL databases. To work around it, you can either create your own `DateTimeField` or use the previous workaround for `MySQLdb`: ```python import pendulum diff --git a/docs/docs/localization.md b/docs/docs/localization.md index b7055ed4..7560dae5 100644 --- a/docs/docs/localization.md +++ b/docs/docs/localization.md @@ -26,7 +26,7 @@ by using `pendulum.set_locale()`. ``` However, you might not want to set the locale globally. The `diff_for_humans()` -method accept a `locale` keyword argument to use a locale for a specific call. +method accepts a `locale` keyword argument to use a locale for a specific call. ```python >>> pendulum.set_locale('de') diff --git a/docs/docs/modifiers.md b/docs/docs/modifiers.md index 160db8c4..b440587a 100644 --- a/docs/docs/modifiers.md +++ b/docs/docs/modifiers.md @@ -1,6 +1,6 @@ # Modifiers -These group of methods perform helpful modifications to a copy of the current instance. +This group of methods performs helpful modifications to a copy of the current instance. You'll notice that the `start_of()`, `next()` and `previous()` methods set the time to `00:00:00` and the `end_of()` methods set the time to `23:59:59.999999`. diff --git a/docs/docs/parsing.md b/docs/docs/parsing.md index 08c3f039..dd78fd45 100644 --- a/docs/docs/parsing.md +++ b/docs/docs/parsing.md @@ -18,10 +18,10 @@ The library natively supports the RFC 3339 format, most ISO 8601 formats and som >>> dt = pendulum.parse('1975-05-21 22:00:00') ``` -If you pass a non-standard or more complicated string, it will raise an exception so it is advised to +If you pass a non-standard or more complicated string, it will raise an exception, so it is advised to use the `from_format()` helper instead. -However, if you want the library to fallback on the [dateutil](https://dateutil.readthedocs.io) parser, +However, if you want the library to fall back on the [dateutil](https://dateutil.readthedocs.io) parser, you have to pass `strict=False`. ```python diff --git a/docs/docs/period.md b/docs/docs/period.md index 523078ed..052966e3 100644 --- a/docs/docs/period.md +++ b/docs/docs/period.md @@ -1,6 +1,6 @@ # Period -When you subtract a `DateTime` instance to another, or use the `diff()` method, it will return a `Period` instance. +When you subtract a `DateTime` instance from another, or use the `diff()` method, it will return a `Period` instance. It inherits from the [Duration](#duration) class with the added benefit that it is aware of the instances that generated it, so that it can give access to more methods and properties: @@ -61,7 +61,7 @@ transitions that might have occurred and adjust accordingly. Let's take an examp !!!warning - Due to its nature (fixed duration between two datetimes), most arithmetic operations will + Due to their nature (fixed duration between two datetimes), most arithmetic operations will return a `Duration` instead of a `Period`. ```python @@ -98,7 +98,7 @@ You can also make an inverted period: ``` If you have inverted dates but want to make sure that the period is positive, -you set the `absolute` keyword argument to `True`: +you should set the `absolute` keyword argument to `True`: ```python >>> period = pendulum.period(end, start, absolute=True) diff --git a/docs/docs/string_formatting.md b/docs/docs/string_formatting.md index ebaeae6c..40afec57 100644 --- a/docs/docs/string_formatting.md +++ b/docs/docs/string_formatting.md @@ -1,6 +1,6 @@ # String formatting -The `__str__` magic method is defined which allows `DateTime` instances to be printed +The `__str__` magic method is defined to allow `DateTime` instances to be printed as a pretty date string when used in a string context. The default string representation is the same as the one returned by the `isoformat()` method. diff --git a/docs/docs/testing.md b/docs/docs/testing.md index 6e3da402..3ba54269 100644 --- a/docs/docs/testing.md +++ b/docs/docs/testing.md @@ -28,7 +28,7 @@ The provided instance will be returned specifically under the following conditio >>> print(pendulum.now()) '2016-07-10T22:10:33.954851-05:00' -Related methods will also returned values mocked according to the **now** instance. +Related methods will also return values mocked according to the **now** instance. ```python >>> print(pendulum.today()) diff --git a/docs/docs/timezones.md b/docs/docs/timezones.md index 0f604020..577198f6 100644 --- a/docs/docs/timezones.md +++ b/docs/docs/timezones.md @@ -1,6 +1,6 @@ # Timezones -Timezones are an important part of every datetime library and `pendulum` +Timezones are an important part of every datetime library, and `pendulum` tries to provide an easy and accurate system to handle them properly. !!!note @@ -107,7 +107,7 @@ with the `in_timezone()` method. of the `fold` attribute which was introduced in Python 3.6. The reason it works inside the Pendulum ecosystem is that it - backported the `fold` attribute in the `DateTime` class. + backports the `fold` attribute in the `DateTime` class. Like said in the introduction, you can use the timezone library directly with standard `datetime` objects but with limitations, especially @@ -134,7 +134,7 @@ by default to determine the transition rule. ``` Instead of relying on the `fold` attribute, you can use the `dst_rule` -keyword argument, this is especially useful if you want to raise errors +keyword argument. This is especially useful if you want to raise errors on non-existing and ambiguous times. ```python @@ -170,8 +170,8 @@ object, things get tricky. '2013-03-31T02:00:00+01:00' ``` -This is not what we expect, it should be `2013-03-31T03:00:00+02:00`. -This is actually easy to retrieve the proper datetime by using `convert()` +This is not what we expect. It should be `2013-03-31T03:00:00+02:00`. +It is actually easy to retrieve the proper datetime by using `convert()` again. ```python From 93563123b020c8bf005bf0168887a013432b2042 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Wamej?= Date: Fri, 6 Mar 2020 11:10:43 +0100 Subject: [PATCH 056/256] fix typo in timestamp resolution (#379) --- docs/docs/string_formatting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/string_formatting.md b/docs/docs/string_formatting.md index 40afec57..ec6e0213 100644 --- a/docs/docs/string_formatting.md +++ b/docs/docs/string_formatting.md @@ -145,7 +145,7 @@ The following tokens are currently supported: | | z | Asia/Baku, Europe/Warsaw, GMT ... | | | zz | EST CST ... MST PST | | **Seconds timestamp** | X | 1381685817, 1234567890.123 | -| **Microseconds timestamp** | x | 1234567890123 | +| **Milliseconds timestamp** | x | 1234567890123 | ### Localized Formats From 9999838ec814b0f968b601ccd451a7edd83c3b28 Mon Sep 17 00:00:00 2001 From: wakemaster39 Date: Fri, 6 Mar 2020 05:22:07 -0500 Subject: [PATCH 057/256] fix: Perioid comparision with other classes and time delta and deprecation warnings (#427) * fix: Perioid comparision with other classes and time delta * fix: cleanup deprecation warnings for regular expression escapes Co-authored-by: Cameron Hurst --- pendulum/formatting/formatter.py | 42 +++++++++++++------------- pendulum/parsing/__init__.py | 12 ++++---- pendulum/parsing/iso8601.py | 34 ++++++++++----------- pendulum/period.py | 13 +++++--- pendulum/tz/local_timezone.py | 4 +-- pendulum/tz/zoneinfo/posix_timezone.py | 26 ++++++++-------- tests/period/test_behavior.py | 9 ++++++ 7 files changed, 76 insertions(+), 64 deletions(-) diff --git a/pendulum/formatting/formatter.py b/pendulum/formatting/formatter.py index d400c287..5678a422 100644 --- a/pendulum/formatting/formatter.py +++ b/pendulum/formatting/formatter.py @@ -11,27 +11,27 @@ from pendulum.utils._compat import decode -_MATCH_1 = "\d" -_MATCH_2 = "\d\d" -_MATCH_3 = "\d{3}" -_MATCH_4 = "\d{4}" -_MATCH_6 = "[+-]?\d{6}" -_MATCH_1_TO_2 = "\d\d?" -_MATCH_1_TO_2_LEFT_PAD = "[0-9 ]\d?" -_MATCH_1_TO_3 = "\d{1,3}" -_MATCH_1_TO_4 = "\d{1,4}" -_MATCH_1_TO_6 = "[+-]?\d{1,6}" -_MATCH_3_TO_4 = "\d{3}\d?" -_MATCH_5_TO_6 = "\d{5}\d?" -_MATCH_UNSIGNED = "\d+" -_MATCH_SIGNED = "[+-]?\d+" -_MATCH_OFFSET = "[Zz]|[+-]\d\d:?\d\d" -_MATCH_SHORT_OFFSET = "[Zz]|[+-]\d\d(?::?\d\d)?" -_MATCH_TIMESTAMP = "[+-]?\d+(\.\d{1,6})?" +_MATCH_1 = r"\d" +_MATCH_2 = r"\d\d" +_MATCH_3 = r"\d{3}" +_MATCH_4 = r"\d{4}" +_MATCH_6 = r"[+-]?\d{6}" +_MATCH_1_TO_2 = r"\d\d?" +_MATCH_1_TO_2_LEFT_PAD = r"[0-9 ]\d?" +_MATCH_1_TO_3 = r"\d{1,3}" +_MATCH_1_TO_4 = r"\d{1,4}" +_MATCH_1_TO_6 = r"[+-]?\d{1,6}" +_MATCH_3_TO_4 = r"\d{3}\d?" +_MATCH_5_TO_6 = r"\d{5}\d?" +_MATCH_UNSIGNED = r"\d+" +_MATCH_SIGNED = r"[+-]?\d+" +_MATCH_OFFSET = r"[Zz]|[+-]\d\d:?\d\d" +_MATCH_SHORT_OFFSET = r"[Zz]|[+-]\d\d(?::?\d\d)?" +_MATCH_TIMESTAMP = r"[+-]?\d+(\.\d{1,6})?" _MATCH_WORD = ( "(?i)[0-9]*" "['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+" - "|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}" + r"|[\u0600-\u06FF/]+(\s*?[\u0600-\u06FF]+){1,2}" ) _MATCH_TIMEZONE = "[A-za-z0-9-+]+(/[A-Za-z0-9-+_]+)?" @@ -39,7 +39,7 @@ class Formatter: _TOKENS = ( - "\[([^\[]*)\]|\\\(.)|" + r"\[([^\[]*)\]|\\(.)|" "(" "Mo|MM?M?M?" "|Do|DDDo|DD?D?D?|ddd?d?|do?" @@ -67,7 +67,7 @@ class Formatter: "Mo": None, "DDDo": None, "Do": lambda locale: tuple( - "\d+{}".format(o) for o in locale.get("custom.ordinal").values() + r"\d+{}".format(o) for o in locale.get("custom.ordinal").values() ), "dddd": "days.wide", "ddd": "days.abbreviated", @@ -615,7 +615,7 @@ def _get_parsed_locale_value( unit = "month" match = "months.abbreviated" elif token == "Do": - parsed["day"] = int(re.match("(\d+)", value).group(1)) + parsed["day"] = int(re.match(r"(\d+)", value).group(1)) return elif token == "dddd": diff --git a/pendulum/parsing/__init__.py b/pendulum/parsing/__init__.py index eef430d6..103fce18 100644 --- a/pendulum/parsing/__init__.py +++ b/pendulum/parsing/__init__.py @@ -24,21 +24,21 @@ "^" "(?P" " (?P" # Classic date (YYYY-MM-DD) - " (?P\d{4})" # Year + r" (?P\d{4})" # Year " (?P" - " (?P[/:])?(?P\d{2})" # Month (optional) - " ((?P[/:])?(?P\d{2}))" # Day (optional) + r" (?P[/:])?(?P\d{2})" # Month (optional) + r" ((?P[/:])?(?P\d{2}))" # Day (optional) " )?" " )" ")?" # Time (optional) "(?P