From 2fdcdb379cacdd32ed5830a9dc0450eab3128781 Mon Sep 17 00:00:00 2001 From: Samuel Searles-Bryant Date: Wed, 13 Oct 2021 23:37:02 +0100 Subject: [PATCH] Make functions private and deprecate public aliases in i18n module Prior to this change, the `Unit` enum and the `abs_timedelta` and `date_and_delta` functions were listed as public functions in the documentation but were not available from the root package: >>> import humanize >>> humanize.gettext Traceback (most recent call last): File "", line 1, in AttributeError: module 'humanize' has no attribute 'gettext' >>> humanize.ngettext Traceback (most recent call last): File "", line 1, in AttributeError: module 'humanize' has no attribute 'ngettext' This change makes these members private. In order to preserve backwards compatibility after this change, we provide aliases for these members which emit deprecation warnings. --- src/humanize/i18n.py | 141 ++++++++++++++++++++++++++++++++++++++--- src/humanize/number.py | 12 ++-- src/humanize/time.py | 30 ++++----- 3 files changed, 153 insertions(+), 30 deletions(-) diff --git a/src/humanize/i18n.py b/src/humanize/i18n.py index 71b5eaf..67c0fca 100644 --- a/src/humanize/i18n.py +++ b/src/humanize/i18n.py @@ -1,6 +1,7 @@ """Activate, get and deactivate translations.""" import gettext as gettext_module import os.path +import warnings from threading import local __all__ = ["activate", "deactivate", "gettext", "ngettext", "thousands_separator"] @@ -66,7 +67,7 @@ def deactivate(): _CURRENT.locale = None -def gettext(message): +def _gettext(message): """Get translation. Args: @@ -78,7 +79,27 @@ def gettext(message): return get_translation().gettext(message) -def pgettext(msgctxt, message): +def gettext(message): + """Get translation. + + Args: + message (str): Text to translate. + + Returns: + str: Translated text. + + WARNING: This function has been deprecated. It is still available as the private + member `_gettext`. + """ + warnings.warn( + "`gettext` has been deprecated. " + "It is still available as the private member `_gettext`.", + DeprecationWarning, + ) + return _gettext(message) + + +def _pgettext(msgctxt, message): """Fetches a particular translation. It works with `msgctxt` .po modifiers and allows duplicate keys with different @@ -103,8 +124,32 @@ def pgettext(msgctxt, message): return message if translation == key else translation -def ngettext(message, plural, num): - """Plural version of gettext. +def pgettext(msgctxt, message): + """Fetches a particular translation. + + It works with `msgctxt` .po modifiers and allows duplicate keys with different + translations. + + Args: + msgctxt (str): Context of the translation. + message (str): Text to translate. + + Returns: + str: Translated text. + + WARNING: This function has been deprecated. It is still available as the private + member `_pgettext`. + """ + warnings.warn( + "`pgettext` has been deprecated. " + "It is still available as the private member `_pgettext`.", + DeprecationWarning, + ) + return _pgettext(msgctxt, message) + + +def _ngettext(message, plural, num): + """Plural version of _gettext. Args: message (str): Singular text to translate. @@ -118,14 +163,37 @@ def ngettext(message, plural, num): return get_translation().ngettext(message, plural, num) -def gettext_noop(message): +def ngettext(msgctxt, message): + """Plural version of gettext. + + Args: + message (str): Singular text to translate. + plural (str): Plural text to translate. + num (str): The number (e.g. item count) to determine translation for the + respective grammatical number. + + Returns: + str: Translated text. + + WARNING: This function has been deprecated. It is still available as the private + member `_ngettext`. + """ + warnings.warn( + "`ngettext` has been deprecated. " + "It is still available as the private member `_ngettext`.", + DeprecationWarning, + ) + return _ngettext(msgctxt, message) + + +def _gettext_noop(message): """Mark a string as a translation string without translating it. Example usage: ```python - CONSTANTS = [gettext_noop('first'), gettext_noop('second')] + CONSTANTS = [_gettext_noop('first'), _gettext_noop('second')] def num_name(n): - return gettext(CONSTANTS[n]) + return _gettext(CONSTANTS[n]) ``` Args: @@ -137,14 +205,41 @@ def num_name(n): return message -def ngettext_noop(singular, plural): +def gettext_noop(message): + """Mark a string as a translation string without translating it. + + Example usage: + ```python + CONSTANTS = [_gettext_noop('first'), _gettext_noop('second')] + def num_name(n): + return _gettext(CONSTANTS[n]) + ``` + + Args: + message (str): Text to translate in the future. + + Returns: + str: Original text, unchanged. + + WARNING: This function has been deprecated. It is still available as the private + member `_gettext_noop`. + """ + warnings.warn( + "`gettext_noop` has been deprecated. " + "It is still available as the private member `_gettext_noop`.", + DeprecationWarning, + ) + return _gettext_noop(message) + + +def _ngettext_noop(singular, plural): """Mark two strings as pluralized translations without translating them. Example usage: ```python CONSTANTS = [ngettext_noop('first', 'firsts'), ngettext_noop('second', 'seconds')] def num_name(n): - return ngettext(*CONSTANTS[n]) + return _ngettext(*CONSTANTS[n]) ``` Args: @@ -157,6 +252,34 @@ def num_name(n): return (singular, plural) +def ngettext_noop(singular, plural): + """Mark two strings as pluralized translations without translating them. + + Example usage: + ```python + CONSTANTS = [ngettext_noop('first', 'firsts'), ngettext_noop('second', 'seconds')] + def num_name(n): + return _ngettext(*CONSTANTS[n]) + ``` + + Args: + singular (str): Singular text to translate in the future. + plural (str): Plural text to translate in the future. + + Returns: + tuple: Original text, unchanged. + + WARNING: This function has been deprecated. It is still available as the private + member `_ngettext_noop`. + """ + warnings.warn( + "`ngettext_noop` has been deprecated. " + "It is still available as the private member `_ngettext_noop`.", + DeprecationWarning, + ) + return _ngettext_noop(singular, plural) + + def thousands_separator() -> str: """Return the thousands separator for a locale, default to comma. diff --git a/src/humanize/number.py b/src/humanize/number.py index e0a7899..be66b84 100644 --- a/src/humanize/number.py +++ b/src/humanize/number.py @@ -6,10 +6,10 @@ import re from fractions import Fraction -from .i18n import gettext as _ -from .i18n import ngettext -from .i18n import ngettext_noop as NS_ -from .i18n import pgettext as P_ +from .i18n import _gettext as _ +from .i18n import _ngettext +from .i18n import _ngettext_noop as NS_ +from .i18n import _pgettext as P_ from .i18n import thousands_separator @@ -201,12 +201,12 @@ def intword(value, format="%.1f"): chopped = value / float(powers[ordinal]) singular, plural = human_powers[ordinal] return ( - " ".join([format, ngettext(singular, plural, math.ceil(chopped))]) + " ".join([format, _ngettext(singular, plural, math.ceil(chopped))]) ) % chopped else: singular, plural = human_powers[ordinal - 1] return ( - " ".join([format, ngettext(singular, plural, math.ceil(chopped))]) + " ".join([format, _ngettext(singular, plural, math.ceil(chopped))]) ) % chopped return str(value) diff --git a/src/humanize/time.py b/src/humanize/time.py index 36ad8d1..249dfcb 100644 --- a/src/humanize/time.py +++ b/src/humanize/time.py @@ -11,8 +11,8 @@ from enum import Enum, EnumMeta from functools import total_ordering -from .i18n import gettext as _ -from .i18n import ngettext +from .i18n import _gettext as _ +from .i18n import _ngettext __all__ = [ "naturaldelta", @@ -209,7 +209,7 @@ def naturaldelta( if seconds == 0: if minimum_unit == _Unit.MICROSECONDS and delta.microseconds < 1000: return ( - ngettext("%d microsecond", "%d microseconds", delta.microseconds) + _ngettext("%d microsecond", "%d microseconds", delta.microseconds) % delta.microseconds ) elif minimum_unit == _Unit.MILLISECONDS or ( @@ -218,52 +218,52 @@ def naturaldelta( ): milliseconds = delta.microseconds / 1000 return ( - ngettext("%d millisecond", "%d milliseconds", milliseconds) + _ngettext("%d millisecond", "%d milliseconds", milliseconds) % milliseconds ) return _("a moment") elif seconds == 1: return _("a second") elif seconds < 60: - return ngettext("%d second", "%d seconds", seconds) % seconds + return _ngettext("%d second", "%d seconds", seconds) % seconds elif 60 <= seconds < 120: return _("a minute") elif 120 <= seconds < 3600: minutes = seconds // 60 - return ngettext("%d minute", "%d minutes", minutes) % minutes + return _ngettext("%d minute", "%d minutes", minutes) % minutes elif 3600 <= seconds < 3600 * 2: return _("an hour") elif 3600 < seconds: hours = seconds // 3600 - return ngettext("%d hour", "%d hours", hours) % hours + return _ngettext("%d hour", "%d hours", hours) % hours elif years == 0: if days == 1: return _("a day") if not use_months: - return ngettext("%d day", "%d days", days) % days + return _ngettext("%d day", "%d days", days) % days else: if not months: - return ngettext("%d day", "%d days", days) % days + return _ngettext("%d day", "%d days", days) % days elif months == 1: return _("a month") else: - return ngettext("%d month", "%d months", months) % months + return _ngettext("%d month", "%d months", months) % months elif years == 1: if not months and not days: return _("a year") elif not months: - return ngettext("1 year, %d day", "1 year, %d days", days) % days + return _ngettext("1 year, %d day", "1 year, %d days", days) % days elif use_months: if months == 1: return _("1 year, 1 month") else: return ( - ngettext("1 year, %d month", "1 year, %d months", months) % months + _ngettext("1 year, %d month", "1 year, %d months", months) % months ) else: - return ngettext("1 year, %d day", "1 year, %d days", days) % days + return _ngettext("1 year, %d day", "1 year, %d days", days) % days else: - return ngettext("%d year", "%d years", years) % years + return _ngettext("%d year", "%d years", years) % years def naturaltime( @@ -601,7 +601,7 @@ def precisedelta(value, minimum_unit="seconds", suppress=(), format="%0.2f") -> for unit, fmt in zip(reversed(_Unit), fmts): singular_txt, plural_txt, value = fmt if value > 0 or (not texts and unit == min_unit): - fmt_txt = ngettext(singular_txt, plural_txt, value) + fmt_txt = _ngettext(singular_txt, plural_txt, value) if unit == min_unit and math.modf(value)[0] > 0: fmt_txt = fmt_txt.replace("%d", format)