Skip to content

Commit

Permalink
Merge pull request #284 from clumio-oss/python-3.12.1-start_time
Browse files Browse the repository at this point in the history
Python 3.12.1 start time
  • Loading branch information
sodul authored Jan 30, 2024
2 parents d1e1bf5 + 3801e72 commit 2301a7f
Show file tree
Hide file tree
Showing 9 changed files with 305 additions and 256 deletions.
12 changes: 6 additions & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
fail-fast: false

steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
Expand All @@ -35,11 +35,11 @@ jobs:
- name: Format
run: black --check --diff green example
if: matrix.python-version == '3.12' && matrix.os == 'ubuntu-latest'
if: matrix.python-version == '3.12.0' && matrix.os == 'ubuntu-latest'

- name: Mypy
run: mypy .
if: matrix.python-version == '3.12' && matrix.os == 'ubuntu-latest'
run: mypy green example
if: matrix.python-version == '3.12.0' && matrix.os == 'ubuntu-latest'

- name: Test
run: |
Expand All @@ -50,10 +50,10 @@ jobs:
run: |
pip install --upgrade coveralls
green -tvvvvr green
if: matrix.python-version == '3.12' && matrix.os == 'ubuntu-latest'
if: matrix.python-version == '3.12.0' && matrix.os == 'ubuntu-latest'

- name: Coveralls
run: coveralls --service=github
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
if: matrix.python-version == '3.12' && matrix.os == 'ubuntu-latest'
if: matrix.python-version == '3.12.0' && matrix.os == 'ubuntu-latest'
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ tags
# virtual environments
venv*
env*
.python-version

*.sublime-workspace

Expand Down
25 changes: 15 additions & 10 deletions green/loader.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from __future__ import annotations

from collections import OrderedDict
from doctest import DocTestSuite
from fnmatch import fnmatch
Expand All @@ -12,7 +14,7 @@
import traceback

from green.output import debug
from green.result import proto_test
from green import result
from green.suite import GreenTestSuite

python_file_pattern = re.compile(r"^[_a-z]\w*?\.py$", re.IGNORECASE)
Expand Down Expand Up @@ -293,7 +295,9 @@ def testFailure(self):
return None


def toProtoTestList(suite, test_list=None, doing_completions=False):
def toProtoTestList(
suite, test_list=None, doing_completions: bool = False
) -> list[result.ProtoTest]:
"""
Take a test suite and turn it into a list of ProtoTests.
Expand All @@ -314,22 +318,22 @@ def toProtoTestList(suite, test_list=None, doing_completions=False):
if isinstance(suite, unittest.TestCase):
# Skip actual blank TestCase objects that twisted inserts
if str(type(suite)) != "<class 'twisted.trial.unittest.TestCase'>":
test_list.append(proto_test(suite))
test_list.append(result.proto_test(suite))
else:
for i in suite:
toProtoTestList(i, test_list, doing_completions)
return test_list


def toParallelTargets(suite, targets):
def toParallelTargets(suite, targets: list[str]) -> list[str]:
"""
Produce a list of targets which should be tested in parallel.
For the most part this will be a list of test modules. The exception is
when a dotted name representing something more granular than a module
was input (like an individal test case or test method)
For the most part, this will be a list of test modules.
The exception is when a dotted name representing something more granular
than a module was input (like an individual test case or test method).
"""
targets = filter(lambda x: x != ".", targets)
targets = [x for x in targets if x != "."]
# First, convert the suite to a proto test list - proto tests nicely
# parse things like the fully dotted name of the test and the
# finest-grained module it belongs to, which simplifies our job.
Expand All @@ -338,11 +342,12 @@ def toParallelTargets(suite, targets):
modules = {x.module for x in proto_test_list}
# Get the list of user-specified targets that are NOT modules
non_module_targets = []
target: str
for target in targets:
if not list(filter(None, [target in x for x in modules])):
if not list(filter(None, (target in x for x in modules))):
non_module_targets.append(target)
# Main loop -- iterating through all loaded test methods
parallel_targets = []
parallel_targets: list[str] = []
for test in proto_test_list:
found = False
for target in non_module_targets:
Expand Down
101 changes: 54 additions & 47 deletions green/output.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
from __future__ import annotations

from typing import Iterable, TYPE_CHECKING

from colorama import Fore, Style
from colorama.ansi import Cursor
from colorama.initialise import wrap_stream
Expand All @@ -8,14 +12,18 @@
import sys
from unidecode import unidecode

if TYPE_CHECKING:
from colorama.ansitowin32 import StreamWrapper
from colorama.initialise import _TextIOT

global debug_level
debug_level = 0

text_type = str
unicode = None # so pyflakes stops complaining


def debug(message, level=1):
def debug(message: str, level: int = 1):
"""
So we can tune how much debug output we get when we turn it on.
"""
Expand All @@ -28,73 +36,72 @@ class Colors:
A class to centralize wrapping strings in terminal colors.
"""

def __init__(self, termcolor=None):
"""
termcolor - If None, attempt to autodetect whether we are in a terminal
and turn on terminal colors if we think we are. If True, force
terminal colors on. If False, force terminal colors off.
def __init__(self, termcolor: bool | None = None):
"""Initialize the Colors object.
Args:
termcolor: If None, attempt to autodetect whether we are in a
terminal and turn on terminal colors if we think we are.
If True, force terminal colors on.
If False, force terminal colors off.
"""
if termcolor is None:
self.termcolor = sys.stdout.isatty()
else:
self.termcolor = termcolor
self.termcolor = sys.stdout.isatty() if termcolor is None else termcolor

def wrap(self, text, style):
def wrap(self, text: str, style: str) -> str:
if self.termcolor:
return f"{style}{text}{Style.RESET_ALL}"
else:
return text
return text

# Movement
def start_of_line(self):
def start_of_line(self) -> str:
return "\r"

def up(self, lines=1):
def up(self, lines: int = 1) -> str:
return Cursor.UP(lines)

# Real colors and styles
def bold(self, text):
def bold(self, text: str) -> str:
return self.wrap(text, Style.BRIGHT)

def blue(self, text):
def blue(self, text: str) -> str:
if platform.system() == "Windows": # pragma: no cover
# Default blue in windows is unreadable (such awful defaults...)
return self.wrap(text, Fore.CYAN)
else:
return self.wrap(text, Fore.BLUE)

def green(self, text):
def green(self, text: str) -> str:
return self.wrap(text, Fore.GREEN)

def red(self, text):
def red(self, text: str) -> str:
return self.wrap(text, Fore.RED)

def yellow(self, text):
def yellow(self, text: str) -> str:
return self.wrap(text, Fore.YELLOW)

# Abstracted colors and styles
def passing(self, text):
def passing(self, text: str) -> str:
return self.green(text)

def failing(self, text):
def failing(self, text: str) -> str:
return self.red(text)

def error(self, text):
def error(self, text: str) -> str:
return self.red(text)

def skipped(self, text):
def skipped(self, text: str) -> str:
return self.blue(text)

def unexpectedSuccess(self, text):
def unexpectedSuccess(self, text: str) -> str:
return self.yellow(text)

def expectedFailure(self, text):
def expectedFailure(self, text: str) -> str:
return self.yellow(text)

def moduleName(self, text):
def moduleName(self, text: str) -> str:
return self.bold(text)

def className(self, text):
def className(self, text: str) -> str:
return text


Expand All @@ -111,19 +118,19 @@ class GreenStream:
writelines(lines)
"""

indent_spaces = 2
_ascii_only_output = False # default to printing output in unicode
indent_spaces: int = 2
_ascii_only_output: bool = False # default to printing output in unicode
coverage_pattern = re.compile(r"TOTAL\s+\d+\s+\d+\s+(?P<percent>\d+)%")

def __init__(
self,
stream,
override_appveyor=False,
disable_windows=False,
disable_unidecode=False,
):
stream: _TextIOT,
override_appveyor: bool = False,
disable_windows: bool = False,
disable_unidecode: bool = False,
) -> None:
self.disable_unidecode = disable_unidecode
self.stream = stream
self.stream: _TextIOT | StreamWrapper = stream
# Ironically, Windows CI platforms such as GitHub Actions and AppVeyor don't support windows
# win32 system calls for colors, but it WILL interpret posix ansi escape codes! (The
# opposite of an actual windows command prompt)
Expand All @@ -135,7 +142,7 @@ def __init__(
if override_appveyor or (
(on_windows and not on_windows_ci) and not disable_windows
): # pragma: no cover
self.stream = wrap_stream(self.stream, None, None, None, True)
self.stream = wrap_stream(stream, None, None, False, True)
# set output is ascii-only
self._ascii_only_output = True
self.closed = False
Expand All @@ -147,23 +154,23 @@ def __init__(
# Susceptible to false-positives if other matching lines are output,
# so set this to None immediately before running a coverage report to
# guarantee accuracy.
self.coverage_percent = None
self.coverage_percent: int | None = None

def flush(self):
def flush(self) -> None:
self.stream.flush()

def writeln(self, text=""):
def writeln(self, text: str = "") -> None:
self.write(text + "\n")

def write(self, text):
if type(text) == bytes:
def write(self, text: str) -> None:
if isinstance(text, bytes):
text = text.decode("utf-8")
# Compensate for windows' anti-social unicode behavior
if self._ascii_only_output and not self.disable_unidecode:
# Windows doesn't actually want unicode, so we get
# the closest ASCII equivalent
text = text_type(unidecode(text))
# Since coverage doesn't like us switching out it's stream to run extra
# Since coverage doesn't like us switching out its stream to run extra
# reports to look for percent covered. We should replace this with
# grabbing the percentage directly from coverage if we can figure out
# how.
Expand All @@ -174,14 +181,14 @@ def write(self, text):
self.coverage_percent = int(percent_str)
self.stream.write(text)

def writelines(self, lines):
def writelines(self, lines: Iterable[str]) -> None:
"""
Just for better compatibility with real file objects
"""
for line in lines:
self.write(line)

def formatText(self, text, indent=0, outcome_char=""):
def formatText(self, text: str, indent: int = 0, outcome_char: str = "") -> str:
# We'll go through each line in the text, modify it, and store it in a
# new list
updated_lines = []
Expand All @@ -197,15 +204,15 @@ def formatText(self, text, indent=0, outcome_char=""):
output = "\n".join(updated_lines)
return output

def formatLine(self, line, indent=0, outcome_char=""):
def formatLine(self, line: str, indent: int = 0, outcome_char: str = "") -> str:
"""
Takes a single line, optionally adds an indent and/or outcome
character to the beginning of the line.
"""
actual_spaces = (indent * self.indent_spaces) - len(outcome_char)
return outcome_char + " " * actual_spaces + line

def isatty(self):
def isatty(self) -> bool:
"""
Wrap internal self.stream.isatty.
"""
Expand Down
Loading

0 comments on commit 2301a7f

Please sign in to comment.