Skip to content
Open
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
3 changes: 3 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ jobs:
steps:
- uses: actions/checkout@v7

- name: Code checks
run: ./scripts/code_checks/run_code_checks.sh --show-diff-on-failure

- name: Set up Python
uses: actions/setup-python@v6
with:
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/release_test_pypi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ jobs:
steps:
- uses: actions/checkout@v7

- name: Code checks
run: ./scripts/code_checks/run_code_checks.sh --show-diff-on-failure

- name: Set up Python
uses: actions/setup-python@v6
with:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/unit_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ jobs:
- name: Run Test
id: tests
run: |
./run_tests.sh test_${{ matrix.python-version }}
./run_tests.sh test_${{ matrix.python-version }}
continue-on-error: true

- name: Archive production artifacts
Expand Down
35 changes: 23 additions & 12 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,17 +1,28 @@
default_stages: [pre-commit, pre-push]

exclude: |
(?x)(
^tests/fixtures/|
^ai_artifacts/|
^.claude/
)

repos:
- repo: git://github.com/pre-commit/pre-commit-hooks
rev: v4.0.1
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v6.0.0
hooks:
- id: end-of-file-fixer
- id: check-toml
- id: check-xml
- id: check-yaml
- id: trailing-whitespace
- id: debug-statements
- id: requirements-txt-fixer
- id: sort-simple-yaml
- id: end-of-file-fixer
- id: check-ast

- repo: git://github.com/PyCQA/flake8
rev: 3.9.2
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.16.5
hooks:
- id: flake8
args: [
'--exclude=tests/fixtures*',
'--ignore=W503,C901',
'--max-line-length=80',
]
- id: ruff
args: [--fix]
- id: ruff-format
2 changes: 1 addition & 1 deletion .readthedocs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ sphinx:
# Explicitly set the version of Python and its requirements
python:
install:
- requirements: docs/requirements.txt
- requirements: docs/requirements.txt
15 changes: 15 additions & 0 deletions Dockerfile_precommit
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# minimum supported version
FROM python:3.10-slim-bookworm

WORKDIR /pycaption

ADD ./.pre-commit-config.yaml /pycaption/
ADD ./ruff.toml /pycaption/

RUN apt-get -y update && \
apt-get -y install git && \
git init && \
pip install pre-commit==4.6.2 && \
pre-commit install --install-hooks

RUN git config --global --add safe.directory /pycaption
5 changes: 3 additions & 2 deletions ai_artifacts/project_understanding.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ and web caption formats. It is the caption ingestion engine behind PBS's video p
| Language | Python (see `setup.py` for version bounds) |
| Framework | None (pure library) |
| Testing | pytest, pytest-lazy-fixture |
| Linting | flake8, pre-commit hooks |
| Linting | ruff (lint + format), pre-commit hooks |
| Packaging | setuptools (setup.py) |
| Dependencies | beautifulsoup4, lxml, cssutils |
| Optional | nltk (transcript features) |
Expand Down Expand Up @@ -91,7 +91,8 @@ pycaption/
├── examples/ # Sample caption files
├── docs/ # Sphinx documentation (introduction.rst)
├── setup.py # Package config (version here)
├── .pre-commit-config.yaml # Linting: end-of-file-fixer, trailing-whitespace, flake8
├── .pre-commit-config.yaml # Hook definitions (pre-commit-hooks + ruff)
├── ruff.toml # Ruff lint/format rules
└── README.rst # Project readme
```

Expand Down
2 changes: 1 addition & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,4 @@ services:
pytest -vvvv --color=yes --junit-xml=junit.xml --cov=pycaption --cov-report xml:coverage.xml;
"
volumes:
- .:/pycaption
- .:/pycaption
5 changes: 5 additions & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
Changelog
---------
2.3.11
^^^^^^
- Replaced the existing linting tools with Ruff
- Added a linting check to the PyPI publishing workflow

2.3.10
^^^^^^^
- Fix ``WebVTTWriter`` producing unparseable output when a single
Expand Down
8 changes: 4 additions & 4 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

import sphinx_rtd_theme


# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
Expand Down Expand Up @@ -45,17 +46,16 @@

# General information about the project.
project = "pycaption"
copyright = "2012-2026, PBS.org " \
"(available under the Apache License, Version 2.0)"
copyright = "2012-2026, PBS.org (available under the Apache License, Version 2.0)"

# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = "2.3.10.dev1"
version = "2.3.11.dev1"
# The full version, including alpha/beta/rc tags.
release = "2.3.10.dev1"
release = "2.3.11.dev1"

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
Expand Down
2 changes: 1 addition & 1 deletion docs/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
readthedocs-sphinx-search==0.3.2
sphinx==7.2.6
sphinx_rtd_theme==1.3.0
readthedocs-sphinx-search==0.3.2
1 change: 1 addition & 0 deletions pycaption/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from .transcript import TranscriptWriter
from .webvtt import WebVTTReader, WebVTTWriter


__all__ = [
"CaptionConverter",
"DFXPReader",
Expand Down
5 changes: 3 additions & 2 deletions pycaption/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@

import logging
import os

from datetime import timedelta
from numbers import Number

from .exceptions import CaptionReadError, CaptionReadTimingError, InvalidInputError


logger = logging.getLogger(__name__)

# `und` a special identifier for an undetermined language according to ISO 639-2
Expand Down Expand Up @@ -40,8 +42,7 @@ def read(self, content, caption_reader):
"""
if not hasattr(caption_reader, "read"):
raise InvalidInputError(
"The caption_reader must be a BaseReader instance "
"with a read() method."
"The caption_reader must be a BaseReader instance with a read() method."
)
self.captions = caption_reader.read(content)
return self
Expand Down
1 change: 1 addition & 0 deletions pycaption/dfxp/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

from ..geometry import Alignment, HorizontalAlignmentEnum, Layout, VerticalAlignmentEnum


DFXP_BASE_MARKUP = """
<tt xmlns="http://www.w3.org/ns/ttml"
xmlns:tts="http://www.w3.org/ns/ttml#styling">
Expand Down
3 changes: 2 additions & 1 deletion pycaption/dfxp/extras.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from .constants import DFXP_DEFAULT_REGION
from .writer import DFXPWriter


LEGACY_DFXP_BASE_MARKUP = """
<tt xmlns="http://www.w3.org/ns/ttml"
xmlns:tts="http://www.w3.org/ns/ttml#styling">
Expand Down Expand Up @@ -255,7 +256,7 @@ def _recreate_span(self, line, node, dfxp):

return line

def _recreate_style(self, content, dfxp):
def _recreate_style(self, content, dfxp): # noqa: C901
"""Convert an internal style dict to DFXP/TTS attributes.

:type content: dict
Expand Down
12 changes: 6 additions & 6 deletions pycaption/dfxp/reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
VERTICAL_ALIGNMENT_TO_DFXP,
)


_LEADING_WHITESPACE_RE = re.compile("^(?:[\n\r]+\\s*)?(.+)")

_DFXP_WRITING_MODE_MAP = {
Expand Down Expand Up @@ -147,7 +148,8 @@ def read(self, content):
style_dict[id_] = self._convert_style(style)

caption_set = CaptionSet(
caption_dict, styles=style_dict,
caption_dict,
styles=style_dict,
visual_alignment_default=HorizontalAlignmentEnum.START,
)

Expand Down Expand Up @@ -179,8 +181,7 @@ def _resolve_explicit_tickrate(self, tt_attrs):
tickrate = float(tt_attrs["ttp:tickrate"])
except ValueError:
raise CaptionReadSyntaxError(
f"ttp:tickRate must be a number, "
f"got '{tt_attrs['ttp:tickrate']}'"
f"ttp:tickRate must be a number, got '{tt_attrs['ttp:tickrate']}'"
)
if tickrate <= 0:
raise CaptionReadSyntaxError(
Expand All @@ -203,8 +204,7 @@ def _resolve_default_tickrate(self, tt_attrs, framerate_str):
framerate_int = int(framerate_str)
except ValueError:
raise CaptionReadSyntaxError(
f"ttp:frameRate must be a positive integer, "
f"got '{framerate_str}'"
f"ttp:frameRate must be a positive integer, got '{framerate_str}'"
)
self.tickrate = float(framerate_int * sub_framerate)

Expand Down Expand Up @@ -396,7 +396,7 @@ def _convert_span_to_nodes(self, tag):
self._convert_tag_to_node(a)

@staticmethod
def _convert_style(tag):
def _convert_style(tag): # noqa: C901
"""Convert DFXP/TTS style attributes on a tag to an internal style dict.

Maps tts:fontStyle, tts:fontWeight, tts:textDecoration, tts:textAlign,
Expand Down
6 changes: 4 additions & 2 deletions pycaption/dfxp/writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"""

import re

from copy import deepcopy
from xml.sax.saxutils import escape

Expand All @@ -26,6 +27,7 @@
_create_external_alignment,
)


_WRITING_DIRECTION_TO_DFXP = {
WritingDirectionEnum.VERTICAL_RL: "tbrl",
WritingDirectionEnum.VERTICAL_LR: "tblr",
Expand Down Expand Up @@ -491,7 +493,7 @@ def _text_shadow_to_outline(value):
return thickness


def _recreate_style(content, dfxp):
def _recreate_style(content, dfxp): # noqa: C901
"""Convert an internal style dict to DFXP/TTS style attributes.

Maps pycaption's internal keys (class, italics, bold, underline, color,
Expand Down Expand Up @@ -542,7 +544,7 @@ def _recreate_style(content, dfxp):
return dfxp_style


def _convert_layout_to_attributes(layout, fallback_alignment=None):
def _convert_layout_to_attributes(layout, fallback_alignment=None): # noqa: C901
"""Convert a Layout object to a dict of DFXP region attributes.

Maps origin, extent, padding, alignment, and writing_direction to their
Expand Down
3 changes: 3 additions & 0 deletions pycaption/geometry.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,15 @@
responsible for the recalculation should return a new object with the
necessary modifications.
"""

import re

from enum import Enum
from functools import total_ordering

from .exceptions import CaptionReadSyntaxError, RelativizationError


_UNIT_MISMATCH_MSG = "The sizes should have the same measure units."


Expand Down
1 change: 1 addition & 0 deletions pycaption/microdvd.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"""

import re

from copy import deepcopy

from .base import (
Expand Down
1 change: 1 addition & 0 deletions pycaption/sami/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

from ..geometry import HorizontalAlignmentEnum


log.setLevel(FATAL)

SAMI_BASE_MARKUP = """
Expand Down
1 change: 1 addition & 0 deletions pycaption/sami/reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from ..geometry import Alignment, HorizontalAlignmentEnum, Layout, Padding, Size
from .parser import SAMIParser


_TAG_TO_STYLE = {"i": "italics", "b": "bold", "u": "underline"}


Expand Down
1 change: 1 addition & 0 deletions pycaption/sami/writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from ..geometry import HorizontalAlignmentEnum
from .constants import HORIZONTAL_ALIGNMENT_MAP, SAMI_BASE_MARKUP


_NON_CSS_KEYS = frozenset(
{
"classes",
Expand Down
1 change: 1 addition & 0 deletions pycaption/scc/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@
from .reader import SCCReader
from .writer import SCC_TOKENS_PER_CAPTION_MAX, SCCWriter


__all__ = ["SCCReader", "SCCWriter", "SCC_TOKENS_PER_CAPTION_MAX"]
2 changes: 2 additions & 0 deletions pycaption/scc/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@
"""

import re as _re

from itertools import product


COMMANDS = {
"9420": "",
"9429": "",
Expand Down
2 changes: 2 additions & 0 deletions pycaption/scc/reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@
"""

import re

from collections import deque
from copy import deepcopy

Expand Down Expand Up @@ -108,6 +109,7 @@
)
from .state_machines import DefaultProvidingPositionTracker


_TIMECODE_RE = re.compile(r"\d{2}:\d{2}:\d{2}[:;](\d{1,2})")


Expand Down
1 change: 1 addition & 0 deletions pycaption/scc/specialized_collections.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
UNDERLINE_COMMANDS,
)


PopOnCue = collections.namedtuple("PopOnCue", "buffer, start, end")

# First two hex chars of SCC codes that produce punctuation ['.', '!', '?', ',']
Expand Down
Loading
Loading