[med-svn] [Git][python-team/packages/enlighten][upstream] New upstream version 1.11.1

Andreas Tille (@tille) gitlab at salsa.debian.org
Thu Nov 3 12:46:15 GMT 2022



Andreas Tille pushed to branch upstream at Debian Python Team / packages / enlighten


Commits:
dc9fc186 by Andreas Tille at 2022-11-03T13:43:18+01:00
New upstream version 1.11.1
- - - - -


30 changed files:

- .github/workflows/tests.yml
- + .sourcery.yaml
- README.rst
- doc/api.rst
- doc/conf.py
- doc/examples.rst
- doc/index.rst
- doc/patterns.rst
- enlighten/__init__.py
- enlighten/_basecounter.py
- enlighten/_basemanager.py
- enlighten/_counter.py
- enlighten/_manager.py
- enlighten/_statusbar.py
- enlighten/_util.py
- enlighten/counter.py
- enlighten/manager.py
- examples/basic.py
- examples/context_manager.py
- examples/floats.py
- examples/ftp_downloader.py
- examples/multicolored.py
- pylintrc
- setup.cfg
- setup.py
- setup_helpers.py
- tests/__init__.py
- tests/test_manager.py
- tests/test_util.py
- tox.ini


Changes:

=====================================
.github/workflows/tests.yml
=====================================
@@ -49,10 +49,10 @@ jobs:
             toxenv: el7
 
           - python-version: pypy-2.7
-            toxenv: pypy
+            toxenv: pypy27
 
-          - python-version: pypy3
-            toxenv: pypy3
+          - python-version: pypy-3.9
+            toxenv: pypy39
 
           - python-version: '3.11-dev'
             optional: true


=====================================
.sourcery.yaml
=====================================
@@ -0,0 +1,9 @@
+refactor:
+  skip:
+    - replace-interpolation-with-fstring  # Python 3.6+
+    - use-contextlib-suppress  # Python 3.4+ (Possibly less efficient)
+    - use-fstring-for-concatenation  # Python 3.6+
+    - use-named-expression  # Python 3.8+
+
+clone_detection:
+  min_lines: 4
\ No newline at end of file


=====================================
README.rst
=====================================
@@ -190,7 +190,7 @@ Advanced
 
 To maintain multiple progress bars simultaneously or write to the console, a manager is required.
 
-Advanced output will only work when the output stream, ``sys.stdout`` by default,
+Advanced output will only work when the output stream, ``sys.__stdout__`` by default,
 is attached to a TTY. get_manager_ can be used to get a manager instance.
 It will return a disabled Manager_ instance if the stream is not attached to a TTY
 and an enabled instance if it is.


=====================================
doc/api.rst
=====================================
@@ -1,5 +1,5 @@
 ..
-  Copyright 2017 - 2020 Avram Lubkin, All Rights Reserved
+  Copyright 2017 - 2021 Avram Lubkin, All Rights Reserved
 
   This Source Code Form is subject to the terms of the Mozilla Public
   License, v. 2.0. If a copy of the MPL was not distributed with this


=====================================
doc/conf.py
=====================================
@@ -36,7 +36,7 @@ extensions = ['sphinx.ext.autodoc',
               'sphinx.ext.intersphinx',
               'sphinx.ext.napoleon']
 
-if not os.environ.get('READTHEDOCS') == 'True':
+if os.environ.get('READTHEDOCS') != 'True':
     extensions.append('sphinxcontrib.spelling')
 
 # Add any paths that contain templates here, relative to this directory.
@@ -53,7 +53,7 @@ master_doc = 'index'
 
 # General information about the project.
 project = 'Enlighten'
-copyright = '2017 - 2021, Avram Lubkin'
+copyright = '2017 - 2022, Avram Lubkin'
 author = 'Avram Lubkin'
 
 # The version info for the project you're documenting, acts as replacement for
@@ -70,7 +70,7 @@ release = version
 #
 # This is also used if you do content translation via gettext catalogs.
 # Usually you set "language" from the command line for these cases.
-language = None
+language = 'en'
 
 # List of patterns, relative to source directory, that match files and
 # directories to ignore when looking for source files.


=====================================
doc/examples.rst
=====================================
@@ -1,5 +1,5 @@
 ..
-  Copyright 2017 - 2021 Avram Lubkin, All Rights Reserved
+  Copyright 2017 - 2022 Avram Lubkin, All Rights Reserved
 
   This Source Code Form is subject to the terms of the Mozilla Public
   License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -31,7 +31,7 @@ Advanced
 
 To maintain multiple progress bars simultaneously or write to the console, a manager is required.
 
-Advanced output will only work when the output stream, :py:data:`sys.stdout` by default,
+Advanced output will only work when the output stream, :py:data:`sys.__stdout__` by default,
 is attached to a TTY. :py:func:`~enlighten.get_manager` can be used to get a manager instance.
 It will return a disabled :py:class:`~enlighten.Manager` instance if the stream is not attached to a TTY
 and an enabled instance if it is.


=====================================
doc/index.rst
=====================================
@@ -1,5 +1,5 @@
 ..
-  Copyright 2017 Avram Lubkin, All Rights Reserved
+  Copyright 2017 - 2021 Avram Lubkin, All Rights Reserved
 
   This Source Code Form is subject to the terms of the Mozilla Public
   License, v. 2.0. If a copy of the MPL was not distributed with this


=====================================
doc/patterns.rst
=====================================
@@ -1,5 +1,5 @@
 ..
-  Copyright 2017 - 2021 Avram Lubkin, All Rights Reserved
+  Copyright 2017 - 2022 Avram Lubkin, All Rights Reserved
 
   This Source Code Form is subject to the terms of the Mozilla Public
   License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -35,7 +35,7 @@ The :py:func:`~enlighten.get_manager` function slightly simplifies this
     import enlighten
 
     # Example configuration object
-    config = {'stream': None,  # Defaults to sys.stdout
+    config = {'stream': None,  # Defaults to sys.__stdout__
               'useCounter': False}
 
     manager = enlighten.get_manager(stream=config['stream'], enabled=config['useCounter'])


=====================================
enlighten/__init__.py
=====================================
@@ -1,5 +1,5 @@
 # -*- coding: utf-8 -*-
-# Copyright 2017 - 2021 Avram Lubkin, All Rights Reserved
+# Copyright 2017 - 2022 Avram Lubkin, All Rights Reserved
 
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -16,7 +16,7 @@ from enlighten.manager import Manager, get_manager
 from enlighten._util import EnlightenWarning, Justify
 
 
-__version__ = '1.10.2'
+__version__ = '1.11.1'
 __all__ = ['Counter', 'EnlightenWarning', 'Justify', 'Manager',
            'StatusBar', 'SubCounter', 'get_manager', 'NotebookManager']
 


=====================================
enlighten/_basecounter.py
=====================================
@@ -1,5 +1,5 @@
 # -*- coding: utf-8 -*-
-# Copyright 2017 - 2020 Avram Lubkin, All Rights Reserved
+# Copyright 2017 - 2022 Avram Lubkin, All Rights Reserved
 
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -104,12 +104,8 @@ class BaseCounter(object):
         If no color is specified for this instance, the content is returned unmodified
         """
 
-        # No color specified
-        if self._color is None:
-            return content
-
-        # Used spec cached by color.setter
-        return self._color[1](content)
+        # Used spec cached by color.setter if available
+        return content if self._color is None else self._color[1](content)
 
     def update(self, *args, **kwargs):
         """


=====================================
enlighten/_basemanager.py
=====================================
@@ -1,5 +1,5 @@
 # -*- coding: utf-8 -*-
-# Copyright 2017 - 2021 Avram Lubkin, All Rights Reserved
+# Copyright 2017 - 2022 Avram Lubkin, All Rights Reserved
 
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -26,7 +26,7 @@ class BaseManager(object):
 
     Args:
         stream(:py:term:`file object`): Output stream. If :py:data:`None`,
-            defaults to :py:data:`sys.stdout`
+            defaults to :py:data:`sys.__stdout__`
         status_bar_class(:py:term:`class`): Status bar class (Default: :py:class:`StatusBar`)
         counter_class(:py:term:`class`): Progress bar class (Default: :py:class:`Counter`)
         set_scroll(bool): Enable scroll area redefinition (Default: :py:data:`True`)
@@ -54,7 +54,7 @@ class BaseManager(object):
         self.no_resize = kwargs.pop('no_resize', False)
         self.set_scroll = kwargs.pop('set_scroll', True)
         self.status_bar_class = kwargs.pop('status_bar_class', StatusBar)
-        self.stream = kwargs.pop('stream', sys.stdout)
+        self.stream = kwargs.pop('stream', sys.__stdout__)
         self.threaded = kwargs.pop('threaded', None)
         self._width = kwargs.pop('width', None)
 


=====================================
enlighten/_counter.py
=====================================
@@ -1,5 +1,5 @@
 # -*- coding: utf-8 -*-
-# Copyright 2017 - 2021 Avram Lubkin, All Rights Reserved
+# Copyright 2017 - 2022 Avram Lubkin, All Rights Reserved
 
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -183,7 +183,7 @@ class Counter(PrintableCounter):
 
     A :py:class:`Counter` instance can be created with the :py:meth:`Manager.counter` method
     or, when a standalone progress bar for simple applications is required, the :py:class:`Counter`
-    class can be called directly. The output stream will default to :py:data:`sys.stdout` unless
+    class can be called directly. The output stream will default to :py:data:`sys.__stdout__` unless
     ``stream`` is set.
 
     .. note::
@@ -433,7 +433,7 @@ class Counter(PrintableCounter):
     """
     # pylint: disable=too-many-instance-attributes
 
-    __slots__ = ('all_fields', 'bar_format', 'counter_format', 'desc', 'fields', 'manager',
+    __slots__ = ('all_fields', 'bar_format', 'counter_format', 'desc', 'fields',
                  'offset', 'series', 'total', 'unit', '_fields', '_subcounters')
     _repr_attrs = ('desc', 'total', 'count', 'unit', 'color')
 
@@ -508,11 +508,7 @@ class Counter(PrintableCounter):
 
             fields['count_%d' % num] = Float(count) if force_float else count
 
-            if self.total and bar_fields:
-                subPercentage = count / float(self.total)
-            else:
-                subPercentage = 0.0
-
+            subPercentage = count / float(self.total) if self.total and bar_fields else 0.0
             if bar_fields:
                 fields['percentage_%d' % num] = subPercentage * 100
 
@@ -696,13 +692,8 @@ class Counter(PrintableCounter):
             percentage = self.count / float(self.total)
             rate = fields['rate']
 
-            # Get eta
-            if rate:
-                # Use iterations so a counter running backwards is accurate
-                fields['eta'] = format_time((self.total - iterations) / rate)
-            else:
-                fields['eta'] = u'?'
-
+            # Get eta. Use iterations so a counter running backwards is accurate
+            fields['eta'] = format_time((self.total - iterations) / rate) if rate else u'?'
         fields['percentage'] = percentage * 100
 
         # Have to go through subcounters here so the fields are available


=====================================
enlighten/_manager.py
=====================================
@@ -1,5 +1,5 @@
 # -*- coding: utf-8 -*-
-# Copyright 2017 - 2021 Avram Lubkin, All Rights Reserved
+# Copyright 2017 - 2022 Avram Lubkin, All Rights Reserved
 
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -30,7 +30,7 @@ class Manager(BaseManager):
 
     Args:
         stream(:py:term:`file object`): Output stream. If :py:data:`None`,
-            defaults to :py:data:`sys.stdout`
+            defaults to :py:data:`sys.__stdout__`
         status_bar_class(:py:term:`class`): Status bar class (Default: :py:class:`StatusBar`)
         counter_class(:py:term:`class`): Progress bar class (Default: :py:class:`Counter`)
         set_scroll(bool): Enable scroll area redefinition (Default: :py:data:`True`)
@@ -57,9 +57,8 @@ class Manager(BaseManager):
         the primary output stream. The cursor position in the companion stream will be
         moved in coordination with the primary stream.
 
-        If the value is :py:data:`None`, :py:data:`sys.stdout` and :py:data:`sys.stderr` will
-        be used as companion streams. Unless explicitly
-        specified, a stream which is not attached to a TTY (the case when
+        If the value is :py:data:`None`, the companion stream will be dynamically determined.
+        Unless explicitly specified, a stream which is not attached to a TTY (the case when
         redirected to a file), will not be used as a companion stream.
 
     """


=====================================
enlighten/_statusbar.py
=====================================
@@ -1,5 +1,5 @@
 # -*- coding: utf-8 -*-
-# Copyright 2017 - 2020 Avram Lubkin, All Rights Reserved
+# Copyright 2017 - 2021 Avram Lubkin, All Rights Reserved
 
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this


=====================================
enlighten/_util.py
=====================================
@@ -1,6 +1,6 @@
 
 # -*- coding: utf-8 -*-
-# Copyright 2017 - 2020 Avram Lubkin, All Rights Reserved
+# Copyright 2017 - 2022 Avram Lubkin, All Rights Reserved
 
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -19,10 +19,17 @@ import re
 import sys
 import warnings
 
-from blessed.colorspace import RGB_256TABLE
+from blessed.colorspace import RGB_256TABLE, X11_COLORNAMES_TO_RGB
 from blessed.sequences import iter_parse
 
 
+try:
+    from functools import lru_cache
+except ImportError:  # pragma: no cover(Python 2)
+    # lru_cache was added in Python 3.2
+    from backports.functools_lru_cache import lru_cache
+
+
 try:
     BASESTRING = basestring
 except NameError:
@@ -136,25 +143,23 @@ class Lookahead:
         return self
 
     def __next__(self):
-        if self.buffer:
-            return self.buffer.pop(0)
-        return next(self.iterator)
+        return self.buffer.pop(0) if self.buffer else next(self.iterator)
 
     # Python 2
     next = __next__
 
-    def lookahead(self, start, stop=None):
-        """
-        Args:
-            start(int): Positive integer index of first value
-            stop(int): Positive integer index to end before (not returned)
-
-        Retrieve next value(s) in iterator.
+    def __getitem__(self, key):
 
-        start and stop roughly behave like slice notation, but must be positive
-        """
+        if isinstance(key, int):
+            first = last = key
+        elif isinstance(key, slice):
+            first = key.start or 0
+            last = max(first, (key.stop or 0) - 1)
+        else:
+            raise TypeError('Index or slice notation is required')
 
-        last = max(start, (stop or 0) - 1)
+        if first < 0:
+            raise ValueError('Negative indexes are not supported')
 
         while last >= len(self.buffer):
             try:
@@ -162,10 +167,7 @@ class Lookahead:
             except StopIteration:
                 break
 
-        if stop is None:
-            return self.buffer[start]
-
-        return self.buffer[start:stop]
+        return self.buffer.__getitem__(key)
 
 
 class Span(list):
@@ -269,8 +271,7 @@ class HTMLConverter(object):
             last_added = to_out[-1] if to_out else None
 
             # Look for normal to close span
-            if value == normal[0] and \
-               normal[1:] == [val[0] for val in parsed.lookahead(0, self.normal_rem or None)]:
+            if value == normal[0] and normal[1:] == [val[0] for val in parsed[: self.normal_rem]]:
 
                 # Clear rest of normal
                 for _ in range(self.normal_rem):
@@ -323,6 +324,38 @@ class HTMLConverter(object):
             4: ('enlighten-underline', {'text-decoration': 'underline'}),
         }
 
+    @property
+    @lru_cache()
+    def rgb_to_colors(self):
+        """
+        Dictionary for translating known RGB values into X11 names
+        """
+
+        rtn = {}
+        for key, val in sorted(X11_COLORNAMES_TO_RGB.items()):
+            val = '#%02x%02x%02x' % val
+            if val not in rtn:
+                rtn[val] = key
+
+        return rtn
+
+    def _color256_lookup(self, idx):
+        """
+        Look up RGB values and attempt to get names in the 256 color space
+        """
+
+        rgb = str(RGB_256TABLE[idx])
+
+        # Some terminals use 256 color syntax for basic colors
+        if 0 <= idx <= 7:  # pragma: no cover(Non-standard Terminal)
+            name = CGA_COLORS[idx]
+        elif 8 <= idx <= 15:  # pragma: no cover(Non-standard Terminal)
+            name = 'bright-%s' % CGA_COLORS[idx - 8]
+        else:
+            name = self.rgb_to_colors.get((rgb[1:3], rgb[3:5], rgb[5:7]), rgb[1:])
+        return name, rgb
+
+    @lru_cache(maxsize=256)
     def _parse_style(self, value, cap):  # pylint: disable=too-many-return-statements
         r"""
         Args:
@@ -337,12 +370,14 @@ class HTMLConverter(object):
         # Parse RGB color foreground
         if cap is caps['color_rgb']:
             rgb = '#%02x%02x%02x' % tuple(int(num) for num in RE_COLOR_RGB.match(value).groups())
-            return 'enlighten-fg-%s' % rgb[1:], {'color': rgb}
+            name = self.rgb_to_colors.get(rgb, rgb[1:])
+            return 'enlighten-fg-%s' % name, {'color': rgb}
 
         # Parse RGB color background
         if cap is caps['on_color_rgb']:
             rgb = '#%02x%02x%02x' % tuple(int(num) for num in RE_ON_COLOR_RGB.match(value).groups())
-            return 'enlighten-bg-%s' % rgb[1:], {'background-color': rgb}
+            name = self.rgb_to_colors.get(rgb, rgb[1:])
+            return 'enlighten-bg-%s' % name, {'background-color': rgb}
 
         # Weird and inconsistent bug that seems to affect Python <= 3.5
         # Matches set_a_attributes3 instead of more specific color 256 patterns
@@ -354,13 +389,13 @@ class HTMLConverter(object):
 
         # Parse 256 color foreground
         if cap is caps['color256']:
-            rgb = str(RGB_256TABLE[int(RE_COLOR_256.match(value).group(1))])
-            return 'enlighten-fg-%s' % rgb[1:], {'color': rgb}
+            name, rgb = self._color256_lookup(int(RE_COLOR_256.match(value).group(1)))
+            return 'enlighten-fg-%s' % name, {'color': rgb}
 
         # Parse 256 color background
         if cap is caps['on_color256']:
-            rgb = str(RGB_256TABLE[int(RE_ON_COLOR_256.match(value).group(1))])
-            return 'enlighten-bg-%s' % rgb[1:], {'background-color': rgb}
+            name, rgb = self._color256_lookup(int(RE_ON_COLOR_256.match(value).group(1)))
+            return 'enlighten-bg-%s' % name, {'background-color': rgb}
 
         # Parse text attributes
         if cap is caps['set_a_attributes1']:


=====================================
enlighten/counter.py
=====================================
@@ -1,5 +1,5 @@
 # -*- coding: utf-8 -*-
-# Copyright 2017 - 2020 Avram Lubkin, All Rights Reserved
+# Copyright 2017 - 2022 Avram Lubkin, All Rights Reserved
 
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -21,13 +21,14 @@ from enlighten.manager import get_manager
 
 # Counter is defined here to avoid circular dependencies
 class Counter(_Counter):  # pylint: disable=missing-docstring
+    # pylint: disable=too-many-instance-attributes
 
     __doc__ = _Counter.__doc__
 
     def __init__(self, **kwargs):
 
-        manager = kwargs.get('manager', None)
-        stream = kwargs.pop('stream', sys.stdout)
+        manager = kwargs.get('manager')
+        stream = kwargs.pop('stream', sys.__stdout__)
 
         if manager is None:
             manager = get_manager(stream=stream, counter_class=self.__class__, set_scroll=False)


=====================================
enlighten/manager.py
=====================================
@@ -1,5 +1,5 @@
 # -*- coding: utf-8 -*-
-# Copyright 2017 - 2021 Avram Lubkin, All Rights Reserved
+# Copyright 2017 - 2022 Avram Lubkin, All Rights Reserved
 
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -29,7 +29,7 @@ def get_manager(stream=None, counter_class=Counter, **kwargs):
     """
     Args:
         stream(:py:term:`file object`): Output stream. If :py:data:`None`,
-            defaults to :py:data:`sys.stdout`
+            defaults to :py:data:`sys.__stdout__`
         counter_class(:py:term:`class`): Progress bar class (Default: :py:class:`Counter`)
         kwargs(Dict[str, Any]): Any additional :py:term:`keyword arguments<keyword argument>`
             will passed to the manager class.
@@ -49,7 +49,7 @@ def get_manager(stream=None, counter_class=Counter, **kwargs):
     if IN_NOTEBOOK:
         return NotebookManager(stream=stream, counter_class=counter_class, **kwargs)
 
-    stream = sys.stdout if stream is None else stream
+    stream = sys.__stdout__ if stream is None else stream
     isatty = hasattr(stream, 'isatty') and stream.isatty()
     kwargs['enabled'] = isatty and kwargs.get('enabled', True)
     return Manager(stream=stream, counter_class=counter_class, **kwargs)


=====================================
examples/basic.py
=====================================
@@ -1,4 +1,4 @@
-# Copyright 2017 Avram Lubkin, All Rights Reserved
+# Copyright 2017 - 2020 Avram Lubkin, All Rights Reserved
 
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this


=====================================
examples/context_manager.py
=====================================
@@ -1,4 +1,4 @@
-# Copyright 2017 Avram Lubkin, All Rights Reserved
+# Copyright 2017 - 2020 Avram Lubkin, All Rights Reserved
 
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this


=====================================
examples/floats.py
=====================================
@@ -1,4 +1,4 @@
-# Copyright 2017 Avram Lubkin, All Rights Reserved
+# Copyright 2017 - 2020 Avram Lubkin, All Rights Reserved
 
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this


=====================================
examples/ftp_downloader.py
=====================================
@@ -1,4 +1,4 @@
-# Copyright 2018 Avram Lubkin, All Rights Reserved
+# Copyright 2018 - 2021 Avram Lubkin, All Rights Reserved
 
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this


=====================================
examples/multicolored.py
=====================================
@@ -1,4 +1,4 @@
-# Copyright 2019 - 2021 Avram Lubkin, All Rights Reserved
+# Copyright 2019 - 2022 Avram Lubkin, All Rights Reserved
 
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -105,7 +105,7 @@ def run_tests(manager, tests=100):
             if result == 7:
                 LOGGER.error("Test %d did not complete", num)
                 errors.update()
-            elif result in (5, 6):
+            elif result in {5, 6}:
                 LOGGER.error("Test %d failed", num)
                 failures.update()
             else:


=====================================
pylintrc
=====================================
@@ -1,16 +1,30 @@
 
 [REPORTS]
+
+# Set the output format. Available formats are text, parseable, colorized, json
+# and msvs (visual studio). You can also give a reporter class, e.g.
+# mypackage.mymodule.MyReporterClass.
 output-format=colorized
 
+
 [FORMAT]
+
 # Use max line length of 100
 max-line-length=100
 
+# Regexp for a line that is allowed to be longer than the limit.
+# URLs and pure strings
+ignore-long-lines=^\s*(# )?<?https?://\S+>?$|^\s*[f|r|u]?b?[\"\'\`].+[\"\'\`]$
+
+
 [DESIGN]
+
 # Maximum number of branch for function / method body.
 max-branches=15
 
+
 [BASIC]
+
 # As far as I can tell, PEP-8 (Nov 1, 2013) does not specify
 # a specific naming convention for variables and arguments
 # prefer mixedcase, starting with a lowercase or underscore
@@ -19,7 +33,22 @@ variable-rgx=[a-z_][A-Za-z0-9_]{1,29}[A-Za-z0-9_]$
 # Good variable names which should always be accepted, separated by a comma.
 good-names=e,_
 
+# Regular expression which should only match function or class names that do
+# not require a docstring.
+no-docstring-rgx=__.*__
+
+
 [MESSAGES CONTROL]
+
+# Disable the message, report, category or checker with the given id(s). You
+# can either give multiple identifiers separated by comma (,) or put this
+# option multiple times (only on the command line, not in the configuration
+# file where it should appear only once). You can also use "--disable=all" to
+# disable everything first and then re-enable specific checks. For example, if
+# you want to run only the similarities checker, you can use "--disable=all
+# --enable=similarities". If you want to run only the classes checker, but have
+# no Warning level messages displayed, use "--disable=all --enable=classes
+# --disable=W".
 disable=
     consider-using-f-string,  # Python 2
     redundant-u-string-prefix,  # Python 2
@@ -27,10 +56,13 @@ disable=
     too-few-public-methods,
     useless-object-inheritance,  # Python 2
 
+
 [SIMILARITIES]
+
 # Minimum lines number of a similarity.
 min-similarity-lines=8
 
+
 [SPELLING]
 
 # Spelling dictionary name. Available dictionaries: en_US (myspell).


=====================================
setup.cfg
=====================================
@@ -10,11 +10,11 @@ python_files = test_*.py
 testpaths = tests
 
 [flake8]
-max-line-length = 100
+max-line-length = 110
 exclude = .svn,CVS,.bzr,.hg,.git,__pycache__,.tox,.eggs,*.egg,build,notes
 
 [pycodestyle]
-max-line-length = 100
+max-line-length = 110
 
 [coverage:run]
 branch = True


=====================================
setup.py
=====================================
@@ -1,5 +1,5 @@
 #!/usr/bin/env python
-# Copyright 2017 - 2021 Avram Lubkin, All Rights Reserved
+# Copyright 2017 - 2022 Avram Lubkin, All Rights Reserved
 
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -17,7 +17,11 @@ from setuptools import setup, find_packages
 
 from setup_helpers import get_version, readme
 
-INSTALL_REQUIRES = ['blessed>=1.17.7', 'prefixed>=0.3.2']
+INSTALL_REQUIRES = [
+    'blessed>=1.17.7',
+    'prefixed>=0.3.2',
+    'backports.functools-lru-cache; python_version < "3.2"'
+]
 TESTS_REQUIRE = ['mock; python_version < "3.3"']
 
 # Additional requirements


=====================================
setup_helpers.py
=====================================
@@ -1,4 +1,4 @@
-# Copyright 2017 - 2020 Avram Lubkin, All Rights Reserved
+# Copyright 2017 - 2022 Avram Lubkin, All Rights Reserved
 
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -8,9 +8,11 @@
 Functions to help with build and setup
 """
 
+import datetime
 import io
 import os
 import re
+import subprocess
 import sys
 
 
@@ -107,6 +109,95 @@ def check_rst2html(path):
     return 0
 
 
+def check_copyrights():
+    """
+    Check files recursively to ensure year of last change is in copyright line
+    """
+
+    this_year = str(datetime.date.today().year)
+    changed_now = []
+
+    # Get list of changed files
+    process = subprocess.run(
+        ('git', 'status', '--porcelain=1'), stdout=subprocess.PIPE, check=True, text=True
+    )
+    for entry in process.stdout.splitlines():
+        filename = entry[3:].strip()
+
+        # Get changes for file
+        process = subprocess.run(
+            ('git', 'diff', '-U0', filename), stdout=subprocess.PIPE, check=True, text=True
+        )
+
+        # Find files with changes that aren't only for copyright
+        for line in process.stdout.splitlines():
+            if line[0] != '+' or line[:3] == '+++':  # Ignore anything but the new contents
+                continue
+
+            if re.search(r'copyright.*20\d\d', line, re.IGNORECASE):  # Ignore copyright line
+                continue
+
+            changed_now.append(filename)
+            break
+
+    # Look for copyright lines
+    process = subprocess.run(
+        ('git', 'grep', '-i', 'copyright'), stdout=subprocess.PIPE, check=True, text=True
+    )
+
+    rtn = 0
+    for entry in process.stdout.splitlines():
+
+        modified = None
+
+        # Get the year in the copyright line
+        match = re.match(r'([^:]+):.*(20\d\d)', entry)
+        if match:
+            filename, year = match.groups()
+
+            # If files is in current changes, use this year
+            if filename in changed_now:
+                modified = this_year
+
+            # Otherwise, try to get the year of last commit that wasn't only updating copyright
+            else:
+                git_log = subprocess.run(
+                    ('git', '--no-pager', 'log', '-U0', filename),
+                    stdout=subprocess.PIPE, check=True, text=True
+                )
+
+                for line in git_log.stdout.splitlines():
+
+                    # Get year
+                    if line.startswith('Date: '):
+                        modified = line.split()[5]
+
+                    # Skip blank line and lines that aren't changes
+                    if not line.strip() or line[0] != '+' or line[:3] == '+++':
+                        continue
+
+                    # Stop looking on the first line we hit that isn't a copyright
+                    if re.search(r'copyright.*20\d\d', line, re.IGNORECASE) is None:
+                        break
+
+            # Special case for Sphinx configuration
+            if filename == 'doc/conf.py' and modified != this_year:
+
+                # Get the latest change date for docs
+                process = subprocess.run(
+                    ('git', 'log', '-1', '--pretty=format:%cs', 'doc/*.rst'),
+                    stdout=subprocess.PIPE, check=True, text=True
+                )
+                modified = process.stdout[:4]
+
+            # Compare modified date to copyright year
+            if modified and modified != year:
+                rtn = 1
+                print('%s [%s]' % (entry, modified))
+
+    return rtn
+
+
 if __name__ == '__main__':
 
     # Do nothing if no arguments were given
@@ -131,6 +222,10 @@ if __name__ == '__main__':
             sys.exit('Missing filename for ReST to HTML check')
         sys.exit(check_rst2html(sys.argv[2]))
 
+    # Print misspelled word list
+    if sys.argv[1] == 'copyright':
+        sys.exit(check_copyrights())
+
     # Unknown option
     else:
         sys.stderr.write('Unknown option: %s' % sys.argv[1])


=====================================
tests/__init__.py
=====================================
@@ -1,5 +1,5 @@
 # -*- coding: utf-8 -*-
-# Copyright 2017 - 2018 Avram Lubkin, All Rights Reserved
+# Copyright 2017 - 2022 Avram Lubkin, All Rights Reserved
 
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -24,23 +24,15 @@ from enlighten._basecounter import BaseCounter
 from enlighten._counter import Counter
 from enlighten._statusbar import StatusBar
 
-# pylint: disable=import-error
+
 if sys.version_info[:2] < (3, 3):
     import mock
 else:
     from unittest import mock  # noqa: F401  # pylint: disable=no-name-in-module
 
-if sys.version_info[0] < 3:
-    from StringIO import StringIO
-    PY2 = True
-else:
-    from io import StringIO
-    PY2 = False
-
-# pylint: enable=import-error
-
 
-OUTPUT = StringIO()
+PY2 = sys.version_info[0] < 3
+OUTPUT = io.StringIO()
 os.environ['TERM'] = 'xterm-256color'  # Default to xterm-256color
 
 


=====================================
tests/test_manager.py
=====================================
@@ -1,5 +1,5 @@
 # -*- coding: utf-8 -*-
-# Copyright 2017 - 2021 Avram Lubkin, All Rights Reserved
+# Copyright 2017 - 2022 Avram Lubkin, All Rights Reserved
 
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -39,7 +39,7 @@ class TestManager(TestCase):
     def test_init_safe(self):
         with redirect_output('stdout', self.tty.stdout):
             # Companion stream is stderr if stream is stdout
-            manager = enlighten.Manager()
+            manager = enlighten.Manager(stream=sys.stdout)
             self.assertIs(manager.stream, sys.stdout)
             self.assertIs(manager.term.stream, sys.stdout)
 
@@ -52,8 +52,8 @@ class TestManager(TestCase):
             mock_stderr.isatty.return_value = True
             manager = enlighten.Manager(stream=sys.__stdout__)
 
-        self.assertIs(manager.stream, sys.stdout)
-        self.assertIs(manager.term.stream, sys.stdout)
+        self.assertIs(manager.stream, sys.__stdout__)
+        self.assertIs(manager.term.stream, sys.__stdout__)
         self.assertIs(manager.companion_stream, mock_stderr)
         self.assertIs(manager.companion_term.stream, mock_stderr)
 
@@ -86,7 +86,7 @@ class TestManager(TestCase):
             # Need to mock isatty() for some build and test environments
             with mock.patch.object(sys, 'stderr') as mock_stderr:
                 mock_stderr.isatty.return_value = True
-                manager = enlighten.Manager()
+                manager = enlighten.Manager(stream=sys.stdout)
 
             self.assertIs(manager.stream, sys.stdout)
             self.assertIs(manager.term.stream, sys.stdout)
@@ -136,7 +136,7 @@ class TestManager(TestCase):
 
     def test_repr(self):
         manager = enlighten.Manager()
-        self.assertEqual(repr(manager), "Manager(stream=%r)" % sys.stdout)
+        self.assertEqual(repr(manager), "Manager(stream=%r)" % sys.__stdout__)
 
     def test_counter_and_remove(self):
         # pylint: disable=no-member,assigning-non-slot
@@ -986,8 +986,8 @@ class TestGetManager(TestCase):
     def test_get_manager_tty(self):
 
         # stdout is attached to a tty
-        with redirect_output('stdout', self.tty.stdout):
-            self.assertTrue(sys.stdout.isatty())
+        with redirect_output('__stdout__', self.tty.stdout):
+            self.assertTrue(sys.__stdout__.isatty())
             manager = enlighten.get_manager(unit='knights')
             self.assertIsInstance(manager, _manager.Manager)
             self.assertTrue('unit' in manager.defaults)
@@ -999,8 +999,8 @@ class TestGetManager(TestCase):
     def test_get_manager_no_tty(self):
 
         # stdout is not attached to a tty
-        with redirect_output('stdout', OUTPUT):
-            self.assertFalse(sys.stdout.isatty())
+        with redirect_output('__stdout__', OUTPUT):
+            self.assertFalse(sys.__stdout__.isatty())
             manager = enlighten.get_manager(unit='knights')
             self.assertIsInstance(manager, _manager.Manager)
             self.assertTrue('unit' in manager.defaults)


=====================================
tests/test_util.py
=====================================
@@ -1,5 +1,5 @@
 # -*- coding: utf-8 -*-
-# Copyright 2017 - 2020 Avram Lubkin, All Rights Reserved
+# Copyright 2017 - 2022 Avram Lubkin, All Rights Reserved
 
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -63,29 +63,42 @@ class TestLookahead(TestCase):
 
         self.assertEqual([next(wrapped) for _ in range(10)], list(range(10)))
 
-    def test_lookahead(self):
-        """Verify lookahead() behavior"""
+    def test_getitem(self):
+        """Verify __getitem__ behavior"""
 
         wrapped = Lookahead(iter(range(10)))
 
-        self.assertEqual(wrapped.lookahead(0), 0)
-        self.assertEqual(wrapped.lookahead(4), 4)
-        self.assertEqual(wrapped.lookahead(2, 4), [2, 3])
-        self.assertEqual(wrapped.lookahead(8, 12), [8, 9])
+        self.assertEqual(wrapped[0], 0)
+        self.assertEqual(wrapped[4], 4)
+        self.assertEqual(wrapped[2: 4], [2, 3])
+        self.assertEqual(wrapped[8: 12], [8, 9])
 
-    def test_lookahead_iteration(self):
-        """lookahead() output changes as iteration proceeds"""
+        with self.assertRaisesRegex(TypeError, 'Index or slice notation is required'):
+            wrapped['named_key']  # pylint: disable=pointless-statement
+
+        with self.assertRaisesRegex(ValueError, 'Negative indexes are not supported'):
+            wrapped[-1]  # pylint: disable=pointless-statement
+
+    def test_buffer(self):
+        """Output changes as iteration proceeds"""
 
         wrapped = Lookahead(iter(range(10)))
 
         self.assertEqual(next(wrapped), 0)
-        self.assertEqual(wrapped.lookahead(0), 1)
+        self.assertEqual(wrapped[0], 1)
         self.assertEqual(next(wrapped), 1)
-        self.assertEqual(wrapped.lookahead(4), 6)
+        self.assertEqual(wrapped[4], 6)
         self.assertEqual(next(wrapped), 2)
-        self.assertEqual(wrapped.lookahead(2, 4), [5, 6])
+        self.assertEqual(wrapped[2: 4], [5, 6])
         self.assertEqual(next(wrapped), 3)
-        self.assertEqual(wrapped.lookahead(8, 12), [])
+        self.assertEqual(wrapped[8: 12], [])
+
+    def test_step_notation(self):
+        """Slice notation is supported"""
+
+        wrapped = Lookahead(iter(range(10)))
+
+        self.assertEqual(wrapped[: 6: 2], [0, 2, 4])
 
 
 class TestHTMLConverter(TestCase):
@@ -99,7 +112,7 @@ class TestHTMLConverter(TestCase):
     def setUpClass(cls):
         cls.tty = MockTTY()
         cls.term = blessed.Terminal(
-            stream=cls.tty.stdout, kind='xterm-256color', force_styling=True
+            stream=cls.tty.stdout, force_styling=True
         )
         cls.term.number_of_colors = 1 << 24
 
@@ -117,22 +130,22 @@ class TestHTMLConverter(TestCase):
         out = self.converter.to_html(self.term.blue_on_aquamarine('blue_on_aquam'))
         self.assertEqual(
             out,
-            '<pre><span class="enlighten-fg-blue enlighten-bg-7fffd4">blue_on_aquam</span></pre>'
+            u'<pre><span class="enlighten-fg-blue enlighten-bg-aquamarine">blue_on_aquam</span></pre>'
         )
 
         self.assertEqual(self.converter._styles['enlighten-fg-blue'], {'color': '#0000ee'})
         self.assertEqual(
-            self.converter._styles['enlighten-bg-7fffd4'], {'background-color': '#7fffd4'}
+            self.converter._styles['enlighten-bg-aquamarine'], {'background-color': '#7fffd4'}
         )
 
         # RGB color on CGA color
         out = self.converter.to_html(self.term.aquamarine_on_blue('aquam_on_blue'))
         self.assertEqual(
             out,
-            '<pre><span class="enlighten-fg-7fffd4 enlighten-bg-blue">aquam_on_blue</span></pre>'
+            u'<pre><span class="enlighten-fg-aquamarine enlighten-bg-blue">aquam_on_blue</span></pre>'
         )
 
-        self.assertEqual(self.converter._styles['enlighten-fg-7fffd4'], {'color': '#7fffd4'})
+        self.assertEqual(self.converter._styles['enlighten-fg-aquamarine'], {'color': '#7fffd4'})
         self.assertEqual(
             self.converter._styles['enlighten-bg-blue'], {'background-color': '#0000ee'}
         )
@@ -199,20 +212,6 @@ class TestHTMLConverter(TestCase):
             self.converter._styles['enlighten-underline'], {'text-decoration': 'underline'}
         )
 
-        # Blink
-        out = self.converter.to_html(self.term.blink('blink'))
-        self.assertEqual(out, '<pre><span class="enlighten-blink">blink</span></pre>')
-
-        self.assertEqual(
-            self.converter._additional_styles,
-            {'@keyframes enlighten-blink-animation {\n  to {\n    visibility: hidden;\n  }\n}'}
-        )
-
-        self.assertEqual(
-            self.converter._styles['enlighten-blink'],
-            {'animation': 'enlighten-blink-animation 1s steps(5, start) infinite'}
-        )
-
     def test_unsupported(self):
         """Verify unsupported does not produce classes"""
 
@@ -247,7 +246,7 @@ class TestHTMLConverter(TestCase):
         out = self.converter.to_html(self.term.blue_on_aquamarine(self.term.blue('blue_on_aquam')))
         self.assertEqual(
             out,
-            '<pre><span class="enlighten-fg-blue enlighten-bg-7fffd4">blue_on_aquam</span></pre>'
+            u'<pre><span class="enlighten-fg-blue enlighten-bg-aquamarine">blue_on_aquam</span></pre>'
         )
 
     def test_style_output(self):
@@ -257,7 +256,7 @@ class TestHTMLConverter(TestCase):
 
         self.assertEqual(
             out,
-            '<pre><span class="enlighten-fg-red enlighten-bg-708090">red_on_slategrey</span></pre>'
+            u'<pre><span class="enlighten-fg-red enlighten-bg-slategray">red_on_slategrey</span></pre>'
         )
 
         style = '''\
@@ -265,7 +264,7 @@ class TestHTMLConverter(TestCase):
         .enlighten-fg-red {
           color: #cd0000;
         }
-        .enlighten-bg-708090 {
+        .enlighten-bg-slategray {
           background-color: #708090;
         }
         </style>
@@ -273,12 +272,25 @@ class TestHTMLConverter(TestCase):
 
         self.assertEqual(self.converter.style, dedent(style))
 
-    def test_style_output_additional(self):
-        """Verify style section output with additional sections"""
+    def test_blink(self):
+        """Blink requires an additional style section"""
+
+        if not self.term.blink:
+            self.skipTest('blink is not supported by this terminal')
 
         out = self.converter.to_html(self.term.blink('blink'))
         self.assertEqual(out, '<pre><span class="enlighten-blink">blink</span></pre>')
 
+        self.assertEqual(
+            self.converter._additional_styles,
+            {'@keyframes enlighten-blink-animation {\n  to {\n    visibility: hidden;\n  }\n}'}
+        )
+
+        self.assertEqual(
+            self.converter._styles['enlighten-blink'],
+            {'animation': 'enlighten-blink-animation 1s steps(5, start) infinite'}
+        )
+
         style = '''\
         <style>
         .enlighten-blink {


=====================================
tox.ini
=====================================
@@ -2,15 +2,17 @@
 ignore_basepython_conflict = True
 envlist =
     lint
+    copyright
     coverage
     docs
     el7
-    py{27,35,36,37,38,39,py,py3}
+    py{27,36,37,38,39,py27,py38}
 
 [base]
 deps =
     blessed
     prefixed
+    py{27,py27}: backports.functools-lru-cache
 
 [ipython]
 deps =
@@ -26,7 +28,7 @@ ignore_errors = True
 
 deps =
     {[base]deps}
-    py{27,py}: mock
+    py{27,py27}: mock
 
 commands =
     {envpython} -m unittest discover -s {toxinidir}/tests {posargs}
@@ -39,6 +41,7 @@ deps =
     prefixed == 0.3.2
     # setuptools == 0.9.8 (Doesn't support PEP 508)
     setuptools == 20.2.2
+    backports.functools-lru-cache == 1.2.1
 
 [testenv:flake8]
 skip_install = True
@@ -73,6 +76,13 @@ commands =
     {envpython} -m nbqa flake8 tests
     {envpython} -m nbqa pylint tests
 
+[testenv:copyright]
+skip_install = True
+ignore_errors = True
+
+commands =
+    {envpython} setup_helpers.py copyright
+
 [testenv:lint]
 skip_install = True
 ignore_errors = True



View it on GitLab: https://salsa.debian.org/python-team/packages/enlighten/-/commit/dc9fc186bc38d53b1161aabde827c4e19d08c269

-- 
View it on GitLab: https://salsa.debian.org/python-team/packages/enlighten/-/commit/dc9fc186bc38d53b1161aabde827c4e19d08c269
You're receiving this email because of your account on salsa.debian.org.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/debian-med-commit/attachments/20221103/49b47f45/attachment-0001.htm>


More information about the debian-med-commit mailing list