Skip to content

Commit

Permalink
increased test coverage
Browse files Browse the repository at this point in the history
  • Loading branch information
wolph committed Oct 3, 2023
1 parent 7f00648 commit a4eb6ca
Show file tree
Hide file tree
Showing 23 changed files with 843 additions and 526 deletions.
1 change: 1 addition & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,4 @@ exclude_lines =
if 0:
if __name__ == .__main__.:
if types.TYPE_CHECKING:
@typing.overload
34 changes: 18 additions & 16 deletions progressbar/bar.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,12 @@

from python_utils import converters, types

import progressbar.terminal
import progressbar.env
import progressbar.terminal.stream

from . import (
base,
terminal,
utils,
widgets,
widgets as widgets_module, # Avoid name collision
Expand Down Expand Up @@ -176,14 +177,14 @@ class DefaultFdMixin(ProgressBarMixinBase):
#: For true (24 bit/16M) color support you can use `COLORTERM=truecolor`.
#: For 256 color support you can use `TERM=xterm-256color`.
#: For 16 colorsupport you can use `TERM=xterm`.
enable_colors: terminal.ColorSupport | bool | None = terminal.color_support
enable_colors: progressbar.env.ColorSupport | bool | None = progressbar.env.COLOR_SUPPORT

def __init__(
self,
fd: base.IO = sys.stderr,
is_terminal: bool | None = None,
line_breaks: bool | None = None,
enable_colors: terminal.ColorSupport | None = None,
enable_colors: progressbar.env.ColorSupport | None = None,
line_offset: int = 0,
**kwargs,
):
Expand All @@ -194,7 +195,7 @@ def __init__(

fd = self._apply_line_offset(fd, line_offset)
self.fd = fd
self.is_ansi_terminal = utils.is_ansi_terminal(fd)
self.is_ansi_terminal = progressbar.env.is_ansi_terminal(fd)
self.is_terminal = self._determine_is_terminal(fd, is_terminal)
self.line_breaks = self._determine_line_breaks(line_breaks)
self.enable_colors = self._determine_enable_colors(enable_colors)
Expand All @@ -216,13 +217,13 @@ def _determine_is_terminal(
is_terminal: bool | None,
) -> bool:
if is_terminal is not None:
return utils.is_terminal(fd, is_terminal)
return progressbar.env.is_terminal(fd, is_terminal)
else:
return utils.is_ansi_terminal(fd)
return progressbar.env.is_ansi_terminal(fd)

def _determine_line_breaks(self, line_breaks: bool | None) -> bool:
if line_breaks is None:
return utils.env_flag(
return progressbar.env.env_flag(
'PROGRESSBAR_LINE_BREAKS',
not self.is_terminal,
)
Expand All @@ -231,32 +232,33 @@ def _determine_line_breaks(self, line_breaks: bool | None) -> bool:

def _determine_enable_colors(
self,
enable_colors: terminal.ColorSupport | None,
) -> terminal.ColorSupport:
enable_colors: progressbar.env.ColorSupport | None,
) -> progressbar.env.ColorSupport:
if enable_colors is None:
colors = (
utils.env_flag('PROGRESSBAR_ENABLE_COLORS'),
utils.env_flag('FORCE_COLOR'),
progressbar.env.env_flag('PROGRESSBAR_ENABLE_COLORS'),
progressbar.env.env_flag('FORCE_COLOR'),
self.is_ansi_terminal,
)

for color_enabled in colors:
if color_enabled is not None:
if color_enabled:
enable_colors = terminal.color_support
enable_colors = progressbar.env.COLOR_SUPPORT
else:
enable_colors = terminal.ColorSupport.NONE
enable_colors = progressbar.env.ColorSupport.NONE
break
else: # pragma: no cover
# This scenario should never occur because `is_ansi_terminal`
# should always be `True` or `False`
raise ValueError('Unable to determine color support')

elif enable_colors is True:
enable_colors = terminal.ColorSupport.XTERM_256
enable_colors = progressbar.env.ColorSupport.XTERM_256
elif enable_colors is False:
enable_colors = terminal.ColorSupport.NONE
elif not isinstance(enable_colors, terminal.ColorSupport):
enable_colors = progressbar.env.ColorSupport.NONE
elif not isinstance(enable_colors,
progressbar.env.ColorSupport):
raise ValueError(f'Invalid color support value: {enable_colors}')

return enable_colors
Expand Down
159 changes: 159 additions & 0 deletions progressbar/env.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
from __future__ import annotations

import enum
import os
import re
import typing

from . import base


@typing.overload
def env_flag(name: str, default: bool) -> bool:
...


@typing.overload
def env_flag(name: str, default: bool | None = None) -> bool | None:
...


def env_flag(name, default=None):
'''
Accepts environt variables formatted as y/n, yes/no, 1/0, true/false,
on/off, and returns it as a boolean.
If the environment variable is not defined, or has an unknown value,
returns `default`
'''
v = os.getenv(name)
if v and v.lower() in ('y', 'yes', 't', 'true', 'on', '1'):
return True
if v and v.lower() in ('n', 'no', 'f', 'false', 'off', '0'):
return False
return default


class ColorSupport(enum.IntEnum):
'''Color support for the terminal.'''

NONE = 0
XTERM = 16
XTERM_256 = 256
XTERM_TRUECOLOR = 16777216

@classmethod
def from_env(cls):
'''Get the color support from the environment.
If any of the environment variables contain `24bit` or `truecolor`,
we will enable true color/24 bit support. If they contain `256`, we
will enable 256 color/8 bit support. If they contain `xterm`, we will
enable 16 color support. Otherwise, we will assume no color support.
If `JUPYTER_COLUMNS` or `JUPYTER_LINES` is set, we will assume true
color support.
Note that the highest available value will be used! Having
`COLORTERM=truecolor` will override `TERM=xterm-256color`.
'''
variables = (
'FORCE_COLOR',
'PROGRESSBAR_ENABLE_COLORS',
'COLORTERM',
'TERM',
)

if os.environ.get('JUPYTER_COLUMNS') or os.environ.get(
'JUPYTER_LINES',
):
# Jupyter notebook always supports true color.
return cls.XTERM_TRUECOLOR

support = cls.NONE
for variable in variables:
value = os.environ.get(variable)
if value is None:
continue
elif value in {'truecolor', '24bit'}:
# Truecolor support, we don't need to check anything else.
support = cls.XTERM_TRUECOLOR
break
elif '256' in value:
support = max(cls.XTERM_256, support)
elif value == 'xterm':
support = max(cls.XTERM, support)

return support


def is_ansi_terminal(
fd: base.IO,
is_terminal: bool | None = None,
) -> bool: # pragma: no cover
if is_terminal is None:
# Jupyter Notebooks define this variable and support progress bars
if 'JPY_PARENT_PID' in os.environ:
is_terminal = True
# This works for newer versions of pycharm only. With older versions
# there is no way to check.
elif os.environ.get('PYCHARM_HOSTED') == '1' and not os.environ.get(
'PYTEST_CURRENT_TEST',
):
is_terminal = True

if is_terminal is None:
# check if we are writing to a terminal or not. typically a file object
# is going to return False if the instance has been overridden and
# isatty has not been defined we have no way of knowing so we will not
# use ansi. ansi terminals will typically define one of the 2
# environment variables.
try:
is_tty = fd.isatty()
# Try and match any of the huge amount of Linux/Unix ANSI consoles
if is_tty and ANSI_TERM_RE.match(os.environ.get('TERM', '')):
is_terminal = True
# ANSICON is a Windows ANSI compatible console
elif 'ANSICON' in os.environ:
is_terminal = True
else:
is_terminal = None
except Exception:
is_terminal = False

return bool(is_terminal)


def is_terminal(fd: base.IO, is_terminal: bool | None = None) -> bool:
if is_terminal is None:
# Full ansi support encompasses what we expect from a terminal
is_terminal = is_ansi_terminal(fd) or None

if is_terminal is None:
# Allow a environment variable override
is_terminal = env_flag('PROGRESSBAR_IS_TERMINAL', None)

if is_terminal is None: # pragma: no cover
# Bare except because a lot can go wrong on different systems. If we do
# get a TTY we know this is a valid terminal
try:
is_terminal = fd.isatty()
except Exception:
is_terminal = False

return bool(is_terminal)


COLOR_SUPPORT = ColorSupport.from_env()
ANSI_TERMS = (
'([xe]|bv)term',
'(sco)?ansi',
'cygwin',
'konsole',
'linux',
'rxvt',
'screen',
'tmux',
'vt(10[02]|220|320)',
)
ANSI_TERM_RE = re.compile(f"^({'|'.join(ANSI_TERMS)})", re.IGNORECASE)
3 changes: 3 additions & 0 deletions progressbar/terminal/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
from __future__ import annotations

from .base import * # noqa F403
from .stream import TextIOOutputWrapper, LineOffsetStreamWrapper, LastLineStream
Loading

0 comments on commit a4eb6ca

Please sign in to comment.