Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 1 addition & 5 deletions .github/workflows/syntax.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,10 @@ jobs:
- name: Ruff Check
run: |
uv run ruff --version
uv run ruff format --check
uv run ruff check

- name: Pyright Check
run: |
uv run pyright --version
uv run pyright

- name: Isort Check
run: |
uv run isort --version
uv run isort --check .
66 changes: 31 additions & 35 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ dev = [
]
build = ["build", "setuptools>=77.0.3"]
test = ["coverage>=7.2.0", "pytest-qt", "pytest-timeout", "pytest>=6.0.0"]
lint = ["isort", "pyright", "ruff"]
lint = ["pyright", "ruff"]

[project.urls]
Homepage = "https://github.com/vkbo/subtle"
Expand All @@ -51,61 +51,57 @@ include = ["subtle_gui*"]
[tool.setuptools.package-data]
subtle_gui = ["assets/*"]

[tool.isort]
py_version = "311"
line_length = 99
wrap_length = 79
multi_line_output = 5
force_grid_wrap = 0
lines_between_types = 1
forced_separate = ["tests.*", "PyQt6.*"]

[tool.ruff]
line-length = 99
line-length = 120

[tool.ruff.lint]
preview = false

# Rules: https://docs.astral.sh/ruff/rules
select = [
"A", # flake8-builtins (A)
"ANN", # flake8-annotations (ANN)
"B", # flake8-bugbear (B)
"E", # pycodestyle (E)
"F", # Pyflakes (F)
"FA", # flake8-future-annotations (FA)
"PERF", # Perflint (PERF)
"PLC", # Pylint Convention (PLC)
"PLE", # Pylint Error (PLE)
"PLW", # Pylint Warning (PLW)
"Q", # flake8-quotes (Q)
"RUF", # Ruff-specific rules (RUF)
"SLF", # flake8-self (SLF)
"SLOT", # flake8-slots (SLOT)
"TC", # flake8-type-checking (TC)
"UP", # pyupgrade (UP)
"W", # pycodestyle (W)
"A", # flake8-builtins (A)
"ANN", # flake8-annotations (ANN)
"B", # flake8-bugbear (B)
"D", # pydocstyle (D)
"E", # pycodestyle (E)
"F", # Pyflakes (F)
"FA", # flake8-future-annotations (FA)
"PERF", # Perflint (PERF)
"PLC", # Pylint Convention (PLC)
"PLE", # Pylint Error (PLE)
"PLR17", # Refactor (PLR) - Only PLR17xx
"PLW", # Pylint Warning (PLW)
"Q", # flake8-quotes (Q)
"RET", # flake8-return (RET)
"RUF", # Ruff-specific rules (RUF)
"SLF", # flake8-self (SLF)
"SLOT", # flake8-slots (SLOT)
"TC", # flake8-type-checking (TC)
"UP", # pyupgrade (UP)
"W", # pycodestyle (W)
]
ignore = [
"ANN401", # any-type
"E221", # multiple-spaces-before-operator
"E226", # missing-whitespace-around-arithmetic-operator
"E228", # missing-whitespace-around-modulo-operator
"E241", # multiple-spaces-after-comma
"E272", # multiple-spaces-before-keyword
"D107", # undocumented-public-init
"D203", # incorrect-blank-line-before-class
"D205", # missing-blank-line-after-summary
"D213", # multi-line-summary-second-line
"PLC0415", # import-outside-top-level
"PLC1901", # compare-to-empty-string
"PLW0108", # unnecessary-lambda
"PLW0717", # too-many-statements-in-try-clause
"PLW2901", # redefined-loop-name
"RET505", # superfluous-else-return
"RUF001", # ambiguous-unicode-character-string
"RUF002", # ambiguous-unicode-character-docstring
"RUF015", # unnecessary-iterable-allocation-for-first-element
"RUF067", # non-empty-init-module
"RUF076", # pytest-fixture-autouse
"UP015", # redundant-open-modes
"UP030", # format-literals
]

[tool.ruff.lint.per-file-ignores]
"tests/*" = ["ANN", "SLF", "TC", "PLC2701"]
"tests/*" = ["ANN", "D101", "D102", "D105", "PLC2701", "RUF069", "SLF", "TC"]

[tool.ruff.format]
quote-style = "double"
Expand Down
4 changes: 3 additions & 1 deletion subtle.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@
"""
Subtle – Start Script
=====================
"""
""" # noqa

import os
import sys

os.curdir = os.path.abspath(os.path.dirname(__file__))

if __name__ == "__main__":
import subtle_gui

subtle_gui.main(sys.argv[1:])
27 changes: 15 additions & 12 deletions subtle_gui/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@

You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
""" # noqa

from __future__ import annotations

import getopt
Expand All @@ -38,6 +39,7 @@
# Package Meta
# ============

# fmt: off
__package__ = "subtle_gui"
__copyright__ = "Copyright (C) Veronica Berglyd Olsen"
__license__ = "GPLv3"
Expand All @@ -46,6 +48,7 @@
__email__ = "code@vkbo.net"
__version__ = "26.1.1"
__date__ = "2026-06-24"
# fmt: on

logger = logging.getLogger(__name__)

Expand All @@ -58,12 +61,12 @@
SHARED = SharedData()

# ANSI Colours
RED = "\033[91m"
GREEN = "\033[92m"
RED = "\033[91m"
GREEN = "\033[92m"
YELLOW = "\033[93m"
BLUE = "\033[94m"
WHITE = "\033[97m"
END = "\033[0m"
BLUE = "\033[94m"
WHITE = "\033[97m"
END = "\033[0m"

# Log Format Components
TIME = "[{asctime:}]"
Expand All @@ -75,7 +78,7 @@

# Read Environment
FORCE_COLOR = bool(os.environ.get("FORCE_COLOR"))
NO_COLOR = bool(os.environ.get("NO_COLOR"))
NO_COLOR = bool(os.environ.get("NO_COLOR"))


def main(sysArgs: list | None = None) -> GuiMain | None:
Expand Down Expand Up @@ -113,7 +116,7 @@ def main(sysArgs: list | None = None) -> GuiMain | None:
# Defaults
logLevel = logging.WARN
fmtColor = FORCE_COLOR
fmtLong = False
fmtLong = False

# Parse Options
try:
Expand Down Expand Up @@ -141,10 +144,10 @@ def main(sysArgs: list | None = None) -> GuiMain | None:
if fmtColor:
# This will overwrite the default level names, and also ensure that
# they can be converted back to integer levels
logging.addLevelName(logging.DEBUG, f"{BLUE}DEBUG{END}")
logging.addLevelName(logging.INFO, f"{GREEN}INFO{END}")
logging.addLevelName(logging.WARNING, f"{YELLOW}WARNING{END}")
logging.addLevelName(logging.ERROR, f"{RED}ERROR{END}")
logging.addLevelName(logging.DEBUG, f"{BLUE}DEBUG{END}")
logging.addLevelName(logging.INFO, f"{GREEN}INFO{END}")
logging.addLevelName(logging.WARNING, f"{YELLOW}WARNING{END}")
logging.addLevelName(logging.ERROR, f"{RED}ERROR{END}")
logging.addLevelName(logging.CRITICAL, f"{RED}CRITICAL{END}")

logTxt = f"{LVLC} {TEXT}" if fmtColor else f"{LVLP} {TEXT}"
Expand Down
36 changes: 14 additions & 22 deletions subtle_gui/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@

You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
""" # noqa

from __future__ import annotations

import json
Expand Down Expand Up @@ -49,7 +50,7 @@ def checkInt(value: Any, default: int) -> int:


def formatInt(value: int) -> str:
"""Formats an integer with k, M, G etc."""
"""Format an integer with k, M, G etc."""
if not isinstance(value, int):
return "ERR"

Expand All @@ -74,11 +75,10 @@ def textCleanup(text: str) -> str:


def regexCleanup(text: str, patterns: list[tuple[re.Pattern, str]]) -> str:
"""Replaces all occurrences of match group 1 in patterns."""
"""Replace all occurrences of match group 1 in patterns."""
for regEx, value in patterns:
matches = [
(s, e, value) for match in regEx.finditer(text)
if (s := match.start(1)) >= 0 and (e := match.end(1)) >= 0
(s, e, value) for match in regEx.finditer(text) if (s := match.start(1)) >= 0 and (e := match.end(1)) >= 0
]
for s, e, value in reversed(matches):
text = text[:s] + value + text[e:]
Expand Down Expand Up @@ -120,22 +120,22 @@ def jsonEncode(data: dict | list | tuple, n: int = 0, nmax: int = 0) -> str:

elif first in ("{", "["):
n += 1
indent = "\n"+" "*n
if n > nmax and nmax > 0:
indent = "\n" + " " * n
if n > nmax > 0:
buffer.append(chunk)
else:
buffer.append(chunk[0] + indent + chunk[1:])

elif first in ("}", "]"):
n -= 1
indent = "\n"+" "*n
if n >= nmax and nmax > 0:
indent = "\n" + " " * n
if n >= nmax > 0:
buffer.append(chunk)
else:
buffer.append(indent + chunk)

elif first == ",":
if n > nmax and nmax > 0:
if n > nmax > 0:
buffer.append(chunk)
else:
buffer.append(chunk[0] + indent + chunk[1:].lstrip())
Expand All @@ -148,8 +148,8 @@ def jsonEncode(data: dict | list | tuple, n: int = 0, nmax: int = 0) -> str:

def formatTS(value: int) -> str:
"""Format millisecond integer as HH:MM:SS,uuu timestamp."""
i, f = value//1000, value%1000
return f"{i//3600:02d}:{i%3600//60:02d}:{i%60:02d},{f:03d}"
i, f = value // 1000, value % 1000
return f"{i // 3600:02d}:{i % 3600 // 60:02d}:{i % 60:02d},{f:03d}"


def decodeTS(value: str | None, default: int = 0, fmt: T_Subs = "SRT") -> int:
Expand All @@ -158,21 +158,13 @@ def decodeTS(value: str | None, default: int = 0, fmt: T_Subs = "SRT") -> int:
if fmt == "SRT" and len(value) >= 12:
if value[2] == ":" and value[5] == ":" and value[8] in ".,":
try:
return (
3600000*int(value[0:2])
+ 60000*int(value[3:5])
+ int(value[6:8] + value[9:12])
)
return 3600000 * int(value[0:2]) + 60000 * int(value[3:5]) + int(value[6:8] + value[9:12])
except Exception:
pass
elif fmt == "SSA" and len(value) == 10:
if value[1] == ":" and value[4] == ":" and value[7] in ":.,":
try:
return (
3600000*int(value[0])
+ 60000*int(value[2:4])
+ 10*int(value[5:7] + value[8:10])
)
return 3600000 * int(value[0]) + 60000 * int(value[2:4]) + 10 * int(value[5:7] + value[8:10])
except Exception:
pass
return default
Loading