Mercurial > repos > shellac > guppy_basecaller
diff env/lib/python3.7/site-packages/humanfriendly/__init__.py @ 5:9b1c78e6ba9c draft default tip
"planemo upload commit 6c0a8142489327ece472c84e558c47da711a9142"
| author | shellac |
|---|---|
| date | Mon, 01 Jun 2020 08:59:25 -0400 |
| parents | 79f47841a781 |
| children |
line wrap: on
line diff
--- a/env/lib/python3.7/site-packages/humanfriendly/__init__.py Thu May 14 16:47:39 2020 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,835 +0,0 @@ -# Human friendly input/output in Python. -# -# Author: Peter Odding <peter@peterodding.com> -# Last Change: April 19, 2020 -# URL: https://humanfriendly.readthedocs.io - -"""The main module of the `humanfriendly` package.""" - -# Standard library modules. -import collections -import datetime -import decimal -import numbers -import os -import os.path -import re -import time - -# Modules included in our package. -from humanfriendly.compat import is_string, monotonic -from humanfriendly.deprecation import define_aliases -from humanfriendly.text import concatenate, format, pluralize, tokenize - -# Public identifiers that require documentation. -__all__ = ( - 'CombinedUnit', - 'InvalidDate', - 'InvalidLength', - 'InvalidSize', - 'InvalidTimespan', - 'SizeUnit', - 'Timer', - '__version__', - 'coerce_boolean', - 'coerce_pattern', - 'coerce_seconds', - 'disk_size_units', - 'format_length', - 'format_number', - 'format_path', - 'format_size', - 'format_timespan', - 'length_size_units', - 'parse_date', - 'parse_length', - 'parse_path', - 'parse_size', - 'parse_timespan', - 'round_number', - 'time_units', -) - -# Semi-standard module versioning. -__version__ = '8.2' - -# Named tuples to define units of size. -SizeUnit = collections.namedtuple('SizeUnit', 'divider, symbol, name') -CombinedUnit = collections.namedtuple('CombinedUnit', 'decimal, binary') - -# Common disk size units in binary (base-2) and decimal (base-10) multiples. -disk_size_units = ( - CombinedUnit(SizeUnit(1000**1, 'KB', 'kilobyte'), SizeUnit(1024**1, 'KiB', 'kibibyte')), - CombinedUnit(SizeUnit(1000**2, 'MB', 'megabyte'), SizeUnit(1024**2, 'MiB', 'mebibyte')), - CombinedUnit(SizeUnit(1000**3, 'GB', 'gigabyte'), SizeUnit(1024**3, 'GiB', 'gibibyte')), - CombinedUnit(SizeUnit(1000**4, 'TB', 'terabyte'), SizeUnit(1024**4, 'TiB', 'tebibyte')), - CombinedUnit(SizeUnit(1000**5, 'PB', 'petabyte'), SizeUnit(1024**5, 'PiB', 'pebibyte')), - CombinedUnit(SizeUnit(1000**6, 'EB', 'exabyte'), SizeUnit(1024**6, 'EiB', 'exbibyte')), - CombinedUnit(SizeUnit(1000**7, 'ZB', 'zettabyte'), SizeUnit(1024**7, 'ZiB', 'zebibyte')), - CombinedUnit(SizeUnit(1000**8, 'YB', 'yottabyte'), SizeUnit(1024**8, 'YiB', 'yobibyte')), -) - -# Common length size units, used for formatting and parsing. -length_size_units = (dict(prefix='nm', divider=1e-09, singular='nm', plural='nm'), - dict(prefix='mm', divider=1e-03, singular='mm', plural='mm'), - dict(prefix='cm', divider=1e-02, singular='cm', plural='cm'), - dict(prefix='m', divider=1, singular='metre', plural='metres'), - dict(prefix='km', divider=1000, singular='km', plural='km')) - -# Common time units, used for formatting of time spans. -time_units = (dict(divider=1e-9, singular='nanosecond', plural='nanoseconds', abbreviations=['ns']), - dict(divider=1e-6, singular='microsecond', plural='microseconds', abbreviations=['us']), - dict(divider=1e-3, singular='millisecond', plural='milliseconds', abbreviations=['ms']), - dict(divider=1, singular='second', plural='seconds', abbreviations=['s', 'sec', 'secs']), - dict(divider=60, singular='minute', plural='minutes', abbreviations=['m', 'min', 'mins']), - dict(divider=60 * 60, singular='hour', plural='hours', abbreviations=['h']), - dict(divider=60 * 60 * 24, singular='day', plural='days', abbreviations=['d']), - dict(divider=60 * 60 * 24 * 7, singular='week', plural='weeks', abbreviations=['w']), - dict(divider=60 * 60 * 24 * 7 * 52, singular='year', plural='years', abbreviations=['y'])) - - -def coerce_boolean(value): - """ - Coerce any value to a boolean. - - :param value: Any Python value. If the value is a string: - - - The strings '1', 'yes', 'true' and 'on' are coerced to :data:`True`. - - The strings '0', 'no', 'false' and 'off' are coerced to :data:`False`. - - Other strings raise an exception. - - Other Python values are coerced using :class:`bool`. - :returns: A proper boolean value. - :raises: :exc:`exceptions.ValueError` when the value is a string but - cannot be coerced with certainty. - """ - if is_string(value): - normalized = value.strip().lower() - if normalized in ('1', 'yes', 'true', 'on'): - return True - elif normalized in ('0', 'no', 'false', 'off', ''): - return False - else: - msg = "Failed to coerce string to boolean! (%r)" - raise ValueError(format(msg, value)) - else: - return bool(value) - - -def coerce_pattern(value, flags=0): - """ - Coerce strings to compiled regular expressions. - - :param value: A string containing a regular expression pattern - or a compiled regular expression. - :param flags: The flags used to compile the pattern (an integer). - :returns: A compiled regular expression. - :raises: :exc:`~exceptions.ValueError` when `value` isn't a string - and also isn't a compiled regular expression. - """ - if is_string(value): - value = re.compile(value, flags) - else: - empty_pattern = re.compile('') - pattern_type = type(empty_pattern) - if not isinstance(value, pattern_type): - msg = "Failed to coerce value to compiled regular expression! (%r)" - raise ValueError(format(msg, value)) - return value - - -def coerce_seconds(value): - """ - Coerce a value to the number of seconds. - - :param value: An :class:`int`, :class:`float` or - :class:`datetime.timedelta` object. - :returns: An :class:`int` or :class:`float` value. - - When `value` is a :class:`datetime.timedelta` object the - :meth:`~datetime.timedelta.total_seconds()` method is called. - """ - if isinstance(value, datetime.timedelta): - return value.total_seconds() - if not isinstance(value, numbers.Number): - msg = "Failed to coerce value to number of seconds! (%r)" - raise ValueError(format(msg, value)) - return value - - -def format_size(num_bytes, keep_width=False, binary=False): - """ - Format a byte count as a human readable file size. - - :param num_bytes: The size to format in bytes (an integer). - :param keep_width: :data:`True` if trailing zeros should not be stripped, - :data:`False` if they can be stripped. - :param binary: :data:`True` to use binary multiples of bytes (base-2), - :data:`False` to use decimal multiples of bytes (base-10). - :returns: The corresponding human readable file size (a string). - - This function knows how to format sizes in bytes, kilobytes, megabytes, - gigabytes, terabytes and petabytes. Some examples: - - >>> from humanfriendly import format_size - >>> format_size(0) - '0 bytes' - >>> format_size(1) - '1 byte' - >>> format_size(5) - '5 bytes' - > format_size(1000) - '1 KB' - > format_size(1024, binary=True) - '1 KiB' - >>> format_size(1000 ** 3 * 4) - '4 GB' - """ - for unit in reversed(disk_size_units): - if num_bytes >= unit.binary.divider and binary: - number = round_number(float(num_bytes) / unit.binary.divider, keep_width=keep_width) - return pluralize(number, unit.binary.symbol, unit.binary.symbol) - elif num_bytes >= unit.decimal.divider and not binary: - number = round_number(float(num_bytes) / unit.decimal.divider, keep_width=keep_width) - return pluralize(number, unit.decimal.symbol, unit.decimal.symbol) - return pluralize(num_bytes, 'byte') - - -def parse_size(size, binary=False): - """ - Parse a human readable data size and return the number of bytes. - - :param size: The human readable file size to parse (a string). - :param binary: :data:`True` to use binary multiples of bytes (base-2) for - ambiguous unit symbols and names, :data:`False` to use - decimal multiples of bytes (base-10). - :returns: The corresponding size in bytes (an integer). - :raises: :exc:`InvalidSize` when the input can't be parsed. - - This function knows how to parse sizes in bytes, kilobytes, megabytes, - gigabytes, terabytes and petabytes. Some examples: - - >>> from humanfriendly import parse_size - >>> parse_size('42') - 42 - >>> parse_size('13b') - 13 - >>> parse_size('5 bytes') - 5 - >>> parse_size('1 KB') - 1000 - >>> parse_size('1 kilobyte') - 1000 - >>> parse_size('1 KiB') - 1024 - >>> parse_size('1 KB', binary=True) - 1024 - >>> parse_size('1.5 GB') - 1500000000 - >>> parse_size('1.5 GB', binary=True) - 1610612736 - """ - tokens = tokenize(size) - if tokens and isinstance(tokens[0], numbers.Number): - # Get the normalized unit (if any) from the tokenized input. - normalized_unit = tokens[1].lower() if len(tokens) == 2 and is_string(tokens[1]) else '' - # If the input contains only a number, it's assumed to be the number of - # bytes. The second token can also explicitly reference the unit bytes. - if len(tokens) == 1 or normalized_unit.startswith('b'): - return int(tokens[0]) - # Otherwise we expect two tokens: A number and a unit. - if normalized_unit: - # Convert plural units to singular units, for details: - # https://github.com/xolox/python-humanfriendly/issues/26 - normalized_unit = normalized_unit.rstrip('s') - for unit in disk_size_units: - # First we check for unambiguous symbols (KiB, MiB, GiB, etc) - # and names (kibibyte, mebibyte, gibibyte, etc) because their - # handling is always the same. - if normalized_unit in (unit.binary.symbol.lower(), unit.binary.name.lower()): - return int(tokens[0] * unit.binary.divider) - # Now we will deal with ambiguous prefixes (K, M, G, etc), - # symbols (KB, MB, GB, etc) and names (kilobyte, megabyte, - # gigabyte, etc) according to the caller's preference. - if (normalized_unit in (unit.decimal.symbol.lower(), unit.decimal.name.lower()) or - normalized_unit.startswith(unit.decimal.symbol[0].lower())): - return int(tokens[0] * (unit.binary.divider if binary else unit.decimal.divider)) - # We failed to parse the size specification. - msg = "Failed to parse size! (input %r was tokenized as %r)" - raise InvalidSize(format(msg, size, tokens)) - - -def format_length(num_metres, keep_width=False): - """ - Format a metre count as a human readable length. - - :param num_metres: The length to format in metres (float / integer). - :param keep_width: :data:`True` if trailing zeros should not be stripped, - :data:`False` if they can be stripped. - :returns: The corresponding human readable length (a string). - - This function supports ranges from nanometres to kilometres. - - Some examples: - - >>> from humanfriendly import format_length - >>> format_length(0) - '0 metres' - >>> format_length(1) - '1 metre' - >>> format_length(5) - '5 metres' - >>> format_length(1000) - '1 km' - >>> format_length(0.004) - '4 mm' - """ - for unit in reversed(length_size_units): - if num_metres >= unit['divider']: - number = round_number(float(num_metres) / unit['divider'], keep_width=keep_width) - return pluralize(number, unit['singular'], unit['plural']) - return pluralize(num_metres, 'metre') - - -def parse_length(length): - """ - Parse a human readable length and return the number of metres. - - :param length: The human readable length to parse (a string). - :returns: The corresponding length in metres (a float). - :raises: :exc:`InvalidLength` when the input can't be parsed. - - Some examples: - - >>> from humanfriendly import parse_length - >>> parse_length('42') - 42 - >>> parse_length('1 km') - 1000 - >>> parse_length('5mm') - 0.005 - >>> parse_length('15.3cm') - 0.153 - """ - tokens = tokenize(length) - if tokens and isinstance(tokens[0], numbers.Number): - # If the input contains only a number, it's assumed to be the number of metres. - if len(tokens) == 1: - return tokens[0] - # Otherwise we expect to find two tokens: A number and a unit. - if len(tokens) == 2 and is_string(tokens[1]): - normalized_unit = tokens[1].lower() - # Try to match the first letter of the unit. - for unit in length_size_units: - if normalized_unit.startswith(unit['prefix']): - return tokens[0] * unit['divider'] - # We failed to parse the length specification. - msg = "Failed to parse length! (input %r was tokenized as %r)" - raise InvalidLength(format(msg, length, tokens)) - - -def format_number(number, num_decimals=2): - """ - Format a number as a string including thousands separators. - - :param number: The number to format (a number like an :class:`int`, - :class:`long` or :class:`float`). - :param num_decimals: The number of decimals to render (2 by default). If no - decimal places are required to represent the number - they will be omitted regardless of this argument. - :returns: The formatted number (a string). - - This function is intended to make it easier to recognize the order of size - of the number being formatted. - - Here's an example: - - >>> from humanfriendly import format_number - >>> print(format_number(6000000)) - 6,000,000 - > print(format_number(6000000000.42)) - 6,000,000,000.42 - > print(format_number(6000000000.42, num_decimals=0)) - 6,000,000,000 - """ - integer_part, _, decimal_part = str(float(number)).partition('.') - reversed_digits = ''.join(reversed(integer_part)) - parts = [] - while reversed_digits: - parts.append(reversed_digits[:3]) - reversed_digits = reversed_digits[3:] - formatted_number = ''.join(reversed(','.join(parts))) - decimals_to_add = decimal_part[:num_decimals].rstrip('0') - if decimals_to_add: - formatted_number += '.' + decimals_to_add - return formatted_number - - -def round_number(count, keep_width=False): - """ - Round a floating point number to two decimal places in a human friendly format. - - :param count: The number to format. - :param keep_width: :data:`True` if trailing zeros should not be stripped, - :data:`False` if they can be stripped. - :returns: The formatted number as a string. If no decimal places are - required to represent the number, they will be omitted. - - The main purpose of this function is to be used by functions like - :func:`format_length()`, :func:`format_size()` and - :func:`format_timespan()`. - - Here are some examples: - - >>> from humanfriendly import round_number - >>> round_number(1) - '1' - >>> round_number(math.pi) - '3.14' - >>> round_number(5.001) - '5' - """ - text = '%.2f' % float(count) - if not keep_width: - text = re.sub('0+$', '', text) - text = re.sub(r'\.$', '', text) - return text - - -def format_timespan(num_seconds, detailed=False, max_units=3): - """ - Format a timespan in seconds as a human readable string. - - :param num_seconds: Any value accepted by :func:`coerce_seconds()`. - :param detailed: If :data:`True` milliseconds are represented separately - instead of being represented as fractional seconds - (defaults to :data:`False`). - :param max_units: The maximum number of units to show in the formatted time - span (an integer, defaults to three). - :returns: The formatted timespan as a string. - :raise: See :func:`coerce_seconds()`. - - Some examples: - - >>> from humanfriendly import format_timespan - >>> format_timespan(0) - '0 seconds' - >>> format_timespan(1) - '1 second' - >>> import math - >>> format_timespan(math.pi) - '3.14 seconds' - >>> hour = 60 * 60 - >>> day = hour * 24 - >>> week = day * 7 - >>> format_timespan(week * 52 + day * 2 + hour * 3) - '1 year, 2 days and 3 hours' - """ - num_seconds = coerce_seconds(num_seconds) - if num_seconds < 60 and not detailed: - # Fast path. - return pluralize(round_number(num_seconds), 'second') - else: - # Slow path. - result = [] - num_seconds = decimal.Decimal(str(num_seconds)) - relevant_units = list(reversed(time_units[0 if detailed else 3:])) - for unit in relevant_units: - # Extract the unit count from the remaining time. - divider = decimal.Decimal(str(unit['divider'])) - count = num_seconds / divider - num_seconds %= divider - # Round the unit count appropriately. - if unit != relevant_units[-1]: - # Integer rounding for all but the smallest unit. - count = int(count) - else: - # Floating point rounding for the smallest unit. - count = round_number(count) - # Only include relevant units in the result. - if count not in (0, '0'): - result.append(pluralize(count, unit['singular'], unit['plural'])) - if len(result) == 1: - # A single count/unit combination. - return result[0] - else: - if not detailed: - # Remove `insignificant' data from the formatted timespan. - result = result[:max_units] - # Format the timespan in a readable way. - return concatenate(result) - - -def parse_timespan(timespan): - """ - Parse a "human friendly" timespan into the number of seconds. - - :param value: A string like ``5h`` (5 hours), ``10m`` (10 minutes) or - ``42s`` (42 seconds). - :returns: The number of seconds as a floating point number. - :raises: :exc:`InvalidTimespan` when the input can't be parsed. - - Note that the :func:`parse_timespan()` function is not meant to be the - "mirror image" of the :func:`format_timespan()` function. Instead it's - meant to allow humans to easily and succinctly specify a timespan with a - minimal amount of typing. It's very useful to accept easy to write time - spans as e.g. command line arguments to programs. - - The time units (and abbreviations) supported by this function are: - - - ms, millisecond, milliseconds - - s, sec, secs, second, seconds - - m, min, mins, minute, minutes - - h, hour, hours - - d, day, days - - w, week, weeks - - y, year, years - - Some examples: - - >>> from humanfriendly import parse_timespan - >>> parse_timespan('42') - 42.0 - >>> parse_timespan('42s') - 42.0 - >>> parse_timespan('1m') - 60.0 - >>> parse_timespan('1h') - 3600.0 - >>> parse_timespan('1d') - 86400.0 - """ - tokens = tokenize(timespan) - if tokens and isinstance(tokens[0], numbers.Number): - # If the input contains only a number, it's assumed to be the number of seconds. - if len(tokens) == 1: - return float(tokens[0]) - # Otherwise we expect to find two tokens: A number and a unit. - if len(tokens) == 2 and is_string(tokens[1]): - normalized_unit = tokens[1].lower() - for unit in time_units: - if (normalized_unit == unit['singular'] or - normalized_unit == unit['plural'] or - normalized_unit in unit['abbreviations']): - return float(tokens[0]) * unit['divider'] - # We failed to parse the timespan specification. - msg = "Failed to parse timespan! (input %r was tokenized as %r)" - raise InvalidTimespan(format(msg, timespan, tokens)) - - -def parse_date(datestring): - """ - Parse a date/time string into a tuple of integers. - - :param datestring: The date/time string to parse. - :returns: A tuple with the numbers ``(year, month, day, hour, minute, - second)`` (all numbers are integers). - :raises: :exc:`InvalidDate` when the date cannot be parsed. - - Supported date/time formats: - - - ``YYYY-MM-DD`` - - ``YYYY-MM-DD HH:MM:SS`` - - .. note:: If you want to parse date/time strings with a fixed, known - format and :func:`parse_date()` isn't useful to you, consider - :func:`time.strptime()` or :meth:`datetime.datetime.strptime()`, - both of which are included in the Python standard library. - Alternatively for more complex tasks consider using the date/time - parsing module in the dateutil_ package. - - Examples: - - >>> from humanfriendly import parse_date - >>> parse_date('2013-06-17') - (2013, 6, 17, 0, 0, 0) - >>> parse_date('2013-06-17 02:47:42') - (2013, 6, 17, 2, 47, 42) - - Here's how you convert the result to a number (`Unix time`_): - - >>> from humanfriendly import parse_date - >>> from time import mktime - >>> mktime(parse_date('2013-06-17 02:47:42') + (-1, -1, -1)) - 1371430062.0 - - And here's how you convert it to a :class:`datetime.datetime` object: - - >>> from humanfriendly import parse_date - >>> from datetime import datetime - >>> datetime(*parse_date('2013-06-17 02:47:42')) - datetime.datetime(2013, 6, 17, 2, 47, 42) - - Here's an example that combines :func:`format_timespan()` and - :func:`parse_date()` to calculate a human friendly timespan since a - given date: - - >>> from humanfriendly import format_timespan, parse_date - >>> from time import mktime, time - >>> unix_time = mktime(parse_date('2013-06-17 02:47:42') + (-1, -1, -1)) - >>> seconds_since_then = time() - unix_time - >>> print(format_timespan(seconds_since_then)) - 1 year, 43 weeks and 1 day - - .. _dateutil: https://dateutil.readthedocs.io/en/latest/parser.html - .. _Unix time: http://en.wikipedia.org/wiki/Unix_time - """ - try: - tokens = [t.strip() for t in datestring.split()] - if len(tokens) >= 2: - date_parts = list(map(int, tokens[0].split('-'))) + [1, 1] - time_parts = list(map(int, tokens[1].split(':'))) + [0, 0, 0] - return tuple(date_parts[0:3] + time_parts[0:3]) - else: - year, month, day = (list(map(int, datestring.split('-'))) + [1, 1])[0:3] - return (year, month, day, 0, 0, 0) - except Exception: - msg = "Invalid date! (expected 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS' but got: %r)" - raise InvalidDate(format(msg, datestring)) - - -def format_path(pathname): - """ - Shorten a pathname to make it more human friendly. - - :param pathname: An absolute pathname (a string). - :returns: The pathname with the user's home directory abbreviated. - - Given an absolute pathname, this function abbreviates the user's home - directory to ``~/`` in order to shorten the pathname without losing - information. It is not an error if the pathname is not relative to the - current user's home directory. - - Here's an example of its usage: - - >>> from os import environ - >>> from os.path import join - >>> vimrc = join(environ['HOME'], '.vimrc') - >>> vimrc - '/home/peter/.vimrc' - >>> from humanfriendly import format_path - >>> format_path(vimrc) - '~/.vimrc' - """ - pathname = os.path.abspath(pathname) - home = os.environ.get('HOME') - if home: - home = os.path.abspath(home) - if pathname.startswith(home): - pathname = os.path.join('~', os.path.relpath(pathname, home)) - return pathname - - -def parse_path(pathname): - """ - Convert a human friendly pathname to an absolute pathname. - - Expands leading tildes using :func:`os.path.expanduser()` and - environment variables using :func:`os.path.expandvars()` and makes the - resulting pathname absolute using :func:`os.path.abspath()`. - - :param pathname: A human friendly pathname (a string). - :returns: An absolute pathname (a string). - """ - return os.path.abspath(os.path.expanduser(os.path.expandvars(pathname))) - - -class Timer(object): - - """ - Easy to use timer to keep track of long during operations. - """ - - def __init__(self, start_time=None, resumable=False): - """ - Remember the time when the :class:`Timer` was created. - - :param start_time: The start time (a float, defaults to the current time). - :param resumable: Create a resumable timer (defaults to :data:`False`). - - When `start_time` is given :class:`Timer` uses :func:`time.time()` as a - clock source, otherwise it uses :func:`humanfriendly.compat.monotonic()`. - """ - if resumable: - self.monotonic = True - self.resumable = True - self.start_time = 0.0 - self.total_time = 0.0 - elif start_time: - self.monotonic = False - self.resumable = False - self.start_time = start_time - else: - self.monotonic = True - self.resumable = False - self.start_time = monotonic() - - def __enter__(self): - """ - Start or resume counting elapsed time. - - :returns: The :class:`Timer` object. - :raises: :exc:`~exceptions.ValueError` when the timer isn't resumable. - """ - if not self.resumable: - raise ValueError("Timer is not resumable!") - self.start_time = monotonic() - return self - - def __exit__(self, exc_type=None, exc_value=None, traceback=None): - """ - Stop counting elapsed time. - - :raises: :exc:`~exceptions.ValueError` when the timer isn't resumable. - """ - if not self.resumable: - raise ValueError("Timer is not resumable!") - if self.start_time: - self.total_time += monotonic() - self.start_time - self.start_time = 0.0 - - def sleep(self, seconds): - """ - Easy to use rate limiting of repeating actions. - - :param seconds: The number of seconds to sleep (an - integer or floating point number). - - This method sleeps for the given number of seconds minus the - :attr:`elapsed_time`. If the resulting duration is negative - :func:`time.sleep()` will still be called, but the argument - given to it will be the number 0 (negative numbers cause - :func:`time.sleep()` to raise an exception). - - The use case for this is to initialize a :class:`Timer` inside - the body of a :keyword:`for` or :keyword:`while` loop and call - :func:`Timer.sleep()` at the end of the loop body to rate limit - whatever it is that is being done inside the loop body. - - For posterity: Although the implementation of :func:`sleep()` only - requires a single line of code I've added it to :mod:`humanfriendly` - anyway because now that I've thought about how to tackle this once I - never want to have to think about it again :-P (unless I find ways to - improve this). - """ - time.sleep(max(0, seconds - self.elapsed_time)) - - @property - def elapsed_time(self): - """ - Get the number of seconds counted so far. - """ - elapsed_time = 0 - if self.resumable: - elapsed_time += self.total_time - if self.start_time: - current_time = monotonic() if self.monotonic else time.time() - elapsed_time += current_time - self.start_time - return elapsed_time - - @property - def rounded(self): - """Human readable timespan rounded to seconds (a string).""" - return format_timespan(round(self.elapsed_time)) - - def __str__(self): - """Show the elapsed time since the :class:`Timer` was created.""" - return format_timespan(self.elapsed_time) - - -class InvalidDate(Exception): - - """ - Raised when a string cannot be parsed into a date. - - For example: - - >>> from humanfriendly import parse_date - >>> parse_date('2013-06-XY') - Traceback (most recent call last): - File "humanfriendly.py", line 206, in parse_date - raise InvalidDate(format(msg, datestring)) - humanfriendly.InvalidDate: Invalid date! (expected 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS' but got: '2013-06-XY') - """ - - -class InvalidSize(Exception): - - """ - Raised when a string cannot be parsed into a file size. - - For example: - - >>> from humanfriendly import parse_size - >>> parse_size('5 Z') - Traceback (most recent call last): - File "humanfriendly/__init__.py", line 267, in parse_size - raise InvalidSize(format(msg, size, tokens)) - humanfriendly.InvalidSize: Failed to parse size! (input '5 Z' was tokenized as [5, 'Z']) - """ - - -class InvalidLength(Exception): - - """ - Raised when a string cannot be parsed into a length. - - For example: - - >>> from humanfriendly import parse_length - >>> parse_length('5 Z') - Traceback (most recent call last): - File "humanfriendly/__init__.py", line 267, in parse_length - raise InvalidLength(format(msg, length, tokens)) - humanfriendly.InvalidLength: Failed to parse length! (input '5 Z' was tokenized as [5, 'Z']) - """ - - -class InvalidTimespan(Exception): - - """ - Raised when a string cannot be parsed into a timespan. - - For example: - - >>> from humanfriendly import parse_timespan - >>> parse_timespan('1 age') - Traceback (most recent call last): - File "humanfriendly/__init__.py", line 419, in parse_timespan - raise InvalidTimespan(format(msg, timespan, tokens)) - humanfriendly.InvalidTimespan: Failed to parse timespan! (input '1 age' was tokenized as [1, 'age']) - """ - - -# Define aliases for backwards compatibility. -define_aliases( - module_name=__name__, - # In humanfriendly 1.23 the format_table() function was added to render a - # table using characters like dashes and vertical bars to emulate borders. - # Since then support for other tables has been added and the name of - # format_table() has changed. - format_table='humanfriendly.tables.format_pretty_table', - # In humanfriendly 1.30 the following text manipulation functions were - # moved out into a separate module to enable their usage in other modules - # of the humanfriendly package (without causing circular imports). - compact='humanfriendly.text.compact', - concatenate='humanfriendly.text.concatenate', - dedent='humanfriendly.text.dedent', - format='humanfriendly.text.format', - is_empty_line='humanfriendly.text.is_empty_line', - pluralize='humanfriendly.text.pluralize', - tokenize='humanfriendly.text.tokenize', - trim_empty_lines='humanfriendly.text.trim_empty_lines', - # In humanfriendly 1.38 the prompt_for_choice() function was moved out into a - # separate module because several variants of interactive prompts were added. - prompt_for_choice='humanfriendly.prompts.prompt_for_choice', - # In humanfriendly 8.0 the Spinner class and minimum_spinner_interval - # variable were extracted to a new module and the erase_line_code, - # hide_cursor_code and show_cursor_code variables were moved. - AutomaticSpinner='humanfriendly.terminal.spinners.AutomaticSpinner', - Spinner='humanfriendly.terminal.spinners.Spinner', - erase_line_code='humanfriendly.terminal.ANSI_ERASE_LINE', - hide_cursor_code='humanfriendly.terminal.ANSI_SHOW_CURSOR', - minimum_spinner_interval='humanfriendly.terminal.spinners.MINIMUM_INTERVAL', - show_cursor_code='humanfriendly.terminal.ANSI_HIDE_CURSOR', -)
