refactor!: rename package from streamer to streamd
- Rename src/streamer/ to src/streamd/ - Update all internal imports - Update pyproject.toml project name and entry point - Update README branding (Streamer -> Strea.md) - Switch from pyright to basedpyright - Bump requires-python to >=3.13
This commit is contained in:
parent
49cd9bcfa0
commit
af2debc19b
23 changed files with 48 additions and 789 deletions
|
|
@ -1 +1 @@
|
|||
3.14.3
|
||||
3.13
|
||||
|
|
|
|||
20
README.md
20
README.md
|
|
@ -1,6 +1,6 @@
|
|||
# streamer
|
||||
# strea.md
|
||||
|
||||
Streamer is a personal knowledge management and time-tracking CLI tool. It organizes time-ordered markdown files using `@tag` annotations, letting you manage tasks, track time, and query your notes from the terminal.
|
||||
Strea.md is a personal knowledge management and time-tracking CLI tool. It organizes time-ordered markdown files using `@tag` annotations, letting you manage tasks, track time, and query your notes from the terminal.
|
||||
|
||||
## Core Concepts
|
||||
|
||||
|
|
@ -12,23 +12,23 @@ Streamer is a personal knowledge management and time-tracking CLI tool. It organ
|
|||
|
||||
Markdown files are named with a timestamp: `YYYYMMDD-HHMMSS [markers].md`
|
||||
|
||||
For example: `20260131-210000 Task Streamer.md`
|
||||
For example: `20260131-210000 Task Streamd.md`
|
||||
|
||||
Within files, `@`-prefixed markers at the beginning of paragraphs or headings define how a shard is categorized.
|
||||
|
||||
## Commands
|
||||
|
||||
- `streamer` / `streamer new` — Create a new timestamped markdown entry, opening your editor
|
||||
- `streamer todo` — Show all open tasks (shards with `@Task` markers)
|
||||
- `streamer edit [number]` — Edit a stream file by index (most recent first)
|
||||
- `streamer timesheet` — Generate time reports from `@Timesheet` markers
|
||||
- `streamd` / `streamd new` — Create a new timestamped markdown entry, opening your editor
|
||||
- `streamd todo` — Show all open tasks (shards with `@Task` markers)
|
||||
- `streamd edit [number]` — Edit a stream file by index (most recent first)
|
||||
- `streamd timesheet` — Generate time reports from `@Timesheet` markers
|
||||
|
||||
## Configuration
|
||||
|
||||
Streamer reads its configuration from `~/.config/streamer/config.yaml` (XDG standard). The main setting is `base_folder`, which points to the directory containing your stream files (defaults to the current working directory).
|
||||
Streamd reads its configuration from `~/.config/streamd/config.yaml` (XDG standard). The main setting is `base_folder`, which points to the directory containing your stream files (defaults to the current working directory).
|
||||
|
||||
## Usage
|
||||
|
||||
Running `streamer` opens your editor to create a new entry. After saving, the file is renamed based on its timestamp and any markers found in the content.
|
||||
Running `streamd` opens your editor to create a new entry. After saving, the file is renamed based on its timestamp and any markers found in the content.
|
||||
|
||||
Running `streamer todo` finds all shards marked as open tasks and displays them as rich-formatted panels in your terminal.
|
||||
Running `streamd todo` finds all shards marked as open tasks and displays them as rich-formatted panels in your terminal.
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
[project]
|
||||
name = "streamer"
|
||||
name = "streamd"
|
||||
version = "0.1.0"
|
||||
description = "Searching for tags in streams"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.12"
|
||||
requires-python = ">=3.13"
|
||||
dependencies = [
|
||||
"click==8.3.1",
|
||||
"mistletoe==1.5.1",
|
||||
|
|
@ -15,7 +15,7 @@ dependencies = [
|
|||
]
|
||||
|
||||
[project.scripts]
|
||||
streamer = "streamer:app"
|
||||
streamd = "streamd:app"
|
||||
|
||||
[build-system]
|
||||
requires = ["hatchling"]
|
||||
|
|
@ -23,8 +23,8 @@ build-backend = "hatchling.build"
|
|||
|
||||
[dependency-groups]
|
||||
dev = [
|
||||
"basedpyright==1.38.0",
|
||||
"faker==40.4.0",
|
||||
"pyright==1.1.408",
|
||||
"pytest==9.0.2",
|
||||
"ruff==0.15.1",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ from __future__ import annotations
|
|||
|
||||
from datetime import datetime
|
||||
|
||||
from streamer.parse.shard import Shard
|
||||
from streamd.parse.shard import Shard
|
||||
|
||||
|
||||
class LocalizedShard(Shard):
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
from streamer.localize.repository_configuration import (
|
||||
from streamd.localize.repository_configuration import (
|
||||
Dimension,
|
||||
Marker,
|
||||
MarkerPlacement,
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
from enum import StrEnum
|
||||
|
||||
from streamer.localize import RepositoryConfiguration
|
||||
from streamer.localize.repository_configuration import (
|
||||
from streamd.localize import RepositoryConfiguration
|
||||
from streamd.localize.repository_configuration import (
|
||||
Dimension,
|
||||
Marker,
|
||||
MarkerPlacement,
|
||||
|
|
@ -2,9 +2,8 @@ from datetime import datetime
|
|||
from itertools import groupby
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
from streamer.localize import LocalizedShard
|
||||
from streamer.query.find import find_shard_by_set_dimension
|
||||
from streamd.localize import LocalizedShard
|
||||
from streamd.query.find import find_shard_by_set_dimension
|
||||
|
||||
from .configuration import TIMESHEET_DIMENSION_NAME, TimesheetPointType
|
||||
from .timecard import SpecialDayType, Timecard, Timesheet
|
||||
|
|
@ -1,126 +0,0 @@
|
|||
import glob
|
||||
import os
|
||||
from datetime import datetime
|
||||
from shutil import move
|
||||
from typing import Annotated, Generator
|
||||
|
||||
import click
|
||||
import typer
|
||||
from rich import print
|
||||
from rich.markdown import Markdown
|
||||
from rich.panel import Panel
|
||||
|
||||
from streamer.localize import (
|
||||
LocalizedShard,
|
||||
RepositoryConfiguration,
|
||||
localize_stream_file,
|
||||
)
|
||||
from streamer.localize.preconfigured_configurations import TaskConfiguration
|
||||
from streamer.parse import parse_markdown_file
|
||||
from streamer.query import find_shard_by_position
|
||||
from streamer.query.find import find_shard_by_set_dimension
|
||||
from streamer.settings import Settings
|
||||
from streamer.timesheet.configuration import BasicTimesheetConfiguration
|
||||
from streamer.timesheet.extract import extract_timesheets
|
||||
|
||||
app = typer.Typer()
|
||||
|
||||
|
||||
def all_files(config: RepositoryConfiguration) -> Generator[LocalizedShard]:
|
||||
for file_name in glob.glob(f"{glob.escape(Settings().base_folder)}/*.md"):
|
||||
with open(file_name, "r") as file:
|
||||
file_content = file.read()
|
||||
if shard := localize_stream_file(
|
||||
parse_markdown_file(file_name, file_content), config
|
||||
):
|
||||
yield shard
|
||||
|
||||
|
||||
@app.command()
|
||||
def todo() -> None:
|
||||
all_shards = list(all_files(TaskConfiguration))
|
||||
|
||||
for task_shard in find_shard_by_position(all_shards, "task", "open"):
|
||||
with open(task_shard.location["file"], "r") as file:
|
||||
file_content = file.read().splitlines()
|
||||
print(
|
||||
Panel(
|
||||
Markdown(
|
||||
"\n".join(
|
||||
file_content[
|
||||
task_shard.start_line - 1 : task_shard.end_line
|
||||
]
|
||||
)
|
||||
),
|
||||
title=f"{task_shard.location['file']}:{task_shard.start_line}",
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@app.command()
|
||||
def edit(number: Annotated[int, typer.Argument()] = 1) -> None:
|
||||
all_shards = list(all_files(TaskConfiguration))
|
||||
sorted_shards = sorted(all_shards, key=lambda s: s.moment)
|
||||
|
||||
if abs(number) >= len(sorted_shards):
|
||||
raise ValueError("Argument out of range")
|
||||
|
||||
selected_number = number
|
||||
if selected_number >= 0:
|
||||
selected_number = len(sorted_shards) - selected_number
|
||||
else:
|
||||
selected_number = -selected_number
|
||||
|
||||
click.edit(None, filename=sorted_shards[selected_number].location["file"])
|
||||
|
||||
|
||||
@app.command()
|
||||
def timesheet() -> None:
|
||||
all_shards = list(all_files(BasicTimesheetConfiguration))
|
||||
sheets = sorted(extract_timesheets(all_shards), key=lambda card: card.date)
|
||||
for sheet in sheets:
|
||||
print(sheet.date)
|
||||
print(
|
||||
",".join(
|
||||
map(lambda card: f"{card.from_time},{card.to_time}", sheet.timecards)
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@app.command()
|
||||
def new() -> None:
|
||||
streamer_directory = Settings().base_folder
|
||||
|
||||
timestamp = datetime.now().strftime("%Y%m%d-%H%M%S")
|
||||
preliminary_file_name = f"{timestamp}_wip.md"
|
||||
prelimary_path = os.path.join(streamer_directory, preliminary_file_name)
|
||||
|
||||
content = "# "
|
||||
with open(prelimary_path, "w") as file:
|
||||
_ = file.write(content)
|
||||
|
||||
click.edit(None, filename=prelimary_path)
|
||||
|
||||
with open(prelimary_path, "r") as file:
|
||||
content = file.read()
|
||||
parsed_content = parse_markdown_file(prelimary_path, content)
|
||||
|
||||
final_file_name = f"{timestamp}.md"
|
||||
if parsed_content.shard is not None and len(
|
||||
markers := parsed_content.shard.markers
|
||||
):
|
||||
final_file_name = f"{timestamp} {' '.join(markers)}.md"
|
||||
|
||||
final_path = os.path.join(streamer_directory, final_file_name)
|
||||
_ = move(prelimary_path, final_path)
|
||||
print(f"Saved as [yellow]{final_file_name}")
|
||||
|
||||
|
||||
@app.callback(invoke_without_command=True)
|
||||
def main(ctx: typer.Context):
|
||||
if ctx.invoked_subcommand is None:
|
||||
new()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app()
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
from datetime import datetime
|
||||
|
||||
from streamer.parse.shard import Shard, StreamFile
|
||||
|
||||
from .extract_datetime import (
|
||||
extract_datetime_from_file_name,
|
||||
extract_datetime_from_marker_list,
|
||||
)
|
||||
from .localized_shard import LocalizedShard
|
||||
from .repository_configuration import RepositoryConfiguration
|
||||
|
||||
|
||||
def localize_shard(
|
||||
shard: Shard,
|
||||
config: RepositoryConfiguration,
|
||||
propagated: dict[str, str],
|
||||
moment: datetime,
|
||||
) -> LocalizedShard:
|
||||
position = {**propagated}
|
||||
private_position: dict[str, str] = {}
|
||||
|
||||
adjusted_moment: datetime = extract_datetime_from_marker_list(shard.markers, moment)
|
||||
|
||||
for marker in shard.markers:
|
||||
if marker in config.markers:
|
||||
marker_definition = config.markers[marker]
|
||||
for placement in marker_definition.placements:
|
||||
if placement.if_with <= set(shard.markers):
|
||||
dimension = config.dimensions[placement.dimension]
|
||||
|
||||
value = placement.value or marker
|
||||
|
||||
if placement.overwrites or (
|
||||
placement.dimension not in position
|
||||
and placement.dimension not in private_position
|
||||
):
|
||||
if dimension.propagate:
|
||||
position[placement.dimension] = value
|
||||
else:
|
||||
private_position[placement.dimension] = value
|
||||
|
||||
children = [
|
||||
localize_shard(child, config, position, adjusted_moment)
|
||||
for child in shard.children
|
||||
]
|
||||
|
||||
position.update(private_position)
|
||||
|
||||
return LocalizedShard(
|
||||
**shard.model_dump(exclude={"children"}),
|
||||
location=position,
|
||||
children=children,
|
||||
moment=adjusted_moment,
|
||||
)
|
||||
|
||||
|
||||
def localize_stream_file(
|
||||
stream_file: StreamFile, config: RepositoryConfiguration
|
||||
) -> LocalizedShard | None:
|
||||
shard_date = extract_datetime_from_file_name(stream_file.file_name)
|
||||
|
||||
if not shard_date or not stream_file.shard:
|
||||
raise ValueError("Could not extract date")
|
||||
|
||||
return localize_shard(
|
||||
stream_file.shard, config, {"file": stream_file.file_name}, shard_date
|
||||
)
|
||||
|
||||
|
||||
__all__ = ["localize_stream_file"]
|
||||
|
|
@ -1,108 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class Dimension(BaseModel):
|
||||
display_name: str
|
||||
comment: Optional[str] = None
|
||||
propagate: bool = False
|
||||
|
||||
|
||||
class MarkerPlacement(BaseModel):
|
||||
if_with: set[str] = set()
|
||||
dimension: str
|
||||
value: str | None = None
|
||||
overwrites: bool = True
|
||||
|
||||
|
||||
class Marker(BaseModel):
|
||||
display_name: str
|
||||
placements: list[MarkerPlacement] = []
|
||||
|
||||
|
||||
class RepositoryConfiguration(BaseModel):
|
||||
dimensions: dict[str, Dimension]
|
||||
markers: dict[str, Marker]
|
||||
|
||||
|
||||
def merge_single_dimension(base: Dimension, second: Dimension) -> Dimension:
|
||||
second_fields_set = getattr(second, "model_fields_set", set())
|
||||
|
||||
return Dimension(
|
||||
display_name=second.display_name or base.display_name,
|
||||
comment=base.comment if second.comment is None else second.comment,
|
||||
propagate=second.propagate
|
||||
if "propagate" in second_fields_set
|
||||
else base.propagate,
|
||||
)
|
||||
|
||||
|
||||
def merge_dimensions(
|
||||
base: dict[str, Dimension], second: dict[str, Dimension]
|
||||
) -> dict[str, Dimension]:
|
||||
merged: dict[str, Dimension] = dict(base)
|
||||
for key, second_dimension in second.items():
|
||||
if key in merged:
|
||||
merged[key] = merge_single_dimension(merged[key], second_dimension)
|
||||
else:
|
||||
merged[key] = second_dimension
|
||||
return merged
|
||||
|
||||
|
||||
def _placement_identity(p: MarkerPlacement) -> tuple[frozenset[str], str]:
|
||||
return (frozenset(p.if_with), p.dimension)
|
||||
|
||||
|
||||
def merge_single_marker(base: Marker, second: Marker) -> Marker:
|
||||
merged_display_name = second.display_name or base.display_name
|
||||
|
||||
merged_placements: list[MarkerPlacement] = []
|
||||
seen: dict[tuple[frozenset[str], str], int] = {}
|
||||
|
||||
for placement in base.placements:
|
||||
ident = _placement_identity(placement)
|
||||
seen[ident] = len(merged_placements)
|
||||
merged_placements.append(placement)
|
||||
|
||||
for placement in second.placements:
|
||||
ident = _placement_identity(placement)
|
||||
if ident in seen:
|
||||
merged_placements[seen[ident]] = placement
|
||||
else:
|
||||
seen[ident] = len(merged_placements)
|
||||
merged_placements.append(placement)
|
||||
|
||||
return Marker(display_name=merged_display_name, placements=merged_placements)
|
||||
|
||||
|
||||
def merge_markers(
|
||||
base: dict[str, Marker], second: dict[str, Marker]
|
||||
) -> dict[str, Marker]:
|
||||
merged: dict[str, Marker] = dict(base)
|
||||
for key, second_marker in second.items():
|
||||
if key in merged:
|
||||
merged[key] = merge_single_marker(merged[key], second_marker)
|
||||
else:
|
||||
merged[key] = second_marker
|
||||
return merged
|
||||
|
||||
|
||||
def merge_repository_configuration(
|
||||
base: RepositoryConfiguration, second: RepositoryConfiguration
|
||||
) -> RepositoryConfiguration:
|
||||
return RepositoryConfiguration(
|
||||
dimensions=merge_dimensions(base.dimensions, second.dimensions),
|
||||
markers=merge_markers(base.markers, second.markers),
|
||||
)
|
||||
|
||||
|
||||
__all__ = [
|
||||
"Dimension",
|
||||
"Marker",
|
||||
"MarkerPlacement",
|
||||
"RepositoryConfiguration",
|
||||
"merge_repository_configuration",
|
||||
]
|
||||
|
|
@ -1,84 +0,0 @@
|
|||
import re
|
||||
from typing import Iterable
|
||||
from mistletoe.block_token import BlockToken
|
||||
from mistletoe.span_token import Emphasis, RawText, Strikethrough, Strong, Link
|
||||
from mistletoe.token import Token
|
||||
|
||||
from .markdown_tag import Tag
|
||||
|
||||
|
||||
def extract_markers_and_tags_from_single_token(
|
||||
token: Token,
|
||||
marker_boundary_encountered: bool,
|
||||
return_at_first_marker: bool = False,
|
||||
) -> tuple[list[str], list[str], bool]:
|
||||
result_markers, result_tags = [], []
|
||||
result_marker_boundary_encountered = marker_boundary_encountered
|
||||
|
||||
if isinstance(token, Tag):
|
||||
if marker_boundary_encountered:
|
||||
result_tags.append(token.content)
|
||||
else:
|
||||
result_markers.append(token.content)
|
||||
elif isinstance(token, (Emphasis, Strong, Strikethrough, Link)):
|
||||
markers, tags, child_marker_boundary_encountered = (
|
||||
extract_markers_and_tags_from_tokens(
|
||||
token.children or [],
|
||||
marker_boundary_encountered,
|
||||
return_at_first_marker,
|
||||
)
|
||||
)
|
||||
result_markers.extend(markers)
|
||||
result_tags.extend(tags)
|
||||
result_marker_boundary_encountered = (
|
||||
marker_boundary_encountered or child_marker_boundary_encountered
|
||||
)
|
||||
elif isinstance(token, RawText) and re.match(r"^[\s]*$", token.content):
|
||||
pass
|
||||
else:
|
||||
result_marker_boundary_encountered = True
|
||||
|
||||
return result_markers, result_tags, result_marker_boundary_encountered
|
||||
|
||||
|
||||
def extract_markers_and_tags_from_tokens(
|
||||
tokens: Iterable[Token],
|
||||
marker_boundary_encountered: bool,
|
||||
return_at_first_marker: bool = False,
|
||||
) -> tuple[list[str], list[str], bool]:
|
||||
result_markers, result_tags = [], []
|
||||
result_marker_boundary_encountered = marker_boundary_encountered
|
||||
|
||||
for child in tokens:
|
||||
markers, tags, child_marker_boundary_encountered = (
|
||||
extract_markers_and_tags_from_single_token(
|
||||
child, result_marker_boundary_encountered, return_at_first_marker
|
||||
)
|
||||
)
|
||||
result_markers.extend(markers)
|
||||
result_tags.extend(tags)
|
||||
result_marker_boundary_encountered = (
|
||||
marker_boundary_encountered or child_marker_boundary_encountered
|
||||
)
|
||||
|
||||
if len(result_markers) > 0 and return_at_first_marker:
|
||||
break
|
||||
|
||||
return result_markers, result_tags, result_marker_boundary_encountered
|
||||
|
||||
|
||||
def extract_markers_and_tags(block_token: BlockToken) -> tuple[list[str], list[str]]:
|
||||
markers, tags, _ = extract_markers_and_tags_from_tokens(
|
||||
block_token.children or [], False
|
||||
)
|
||||
return markers, tags
|
||||
|
||||
|
||||
def has_markers(block_token: BlockToken) -> bool:
|
||||
markers, _, _ = extract_markers_and_tags_from_tokens(
|
||||
block_token.children or [], False, return_at_first_marker=True
|
||||
)
|
||||
return len(markers) > 0
|
||||
|
||||
|
||||
__all__ = ["extract_markers_and_tags", "has_markers"]
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
import re
|
||||
from mistletoe.markdown_renderer import Fragment, MarkdownRenderer
|
||||
from mistletoe.span_token import SpanToken
|
||||
|
||||
|
||||
class Tag(SpanToken):
|
||||
parse_inner = False
|
||||
pattern = re.compile(r"@([^\s*\x60~\[\]]+)")
|
||||
|
||||
|
||||
class TagMarkdownRenderer(MarkdownRenderer):
|
||||
def __init__(self):
|
||||
super().__init__(Tag)
|
||||
|
||||
def render_tag(self, token: Tag):
|
||||
yield Fragment("@")
|
||||
yield Fragment(token.content)
|
||||
|
||||
|
||||
__all__ = ["Tag", "TagMarkdownRenderer"]
|
||||
|
|
@ -1,242 +0,0 @@
|
|||
from collections import Counter
|
||||
|
||||
from mistletoe.block_token import (
|
||||
BlockToken,
|
||||
Document,
|
||||
Heading,
|
||||
List,
|
||||
ListItem,
|
||||
Paragraph,
|
||||
)
|
||||
|
||||
from .extract_tag import extract_markers_and_tags, has_markers
|
||||
from .list import split_at
|
||||
from .markdown_tag import TagMarkdownRenderer
|
||||
from .shard import Shard, StreamFile
|
||||
|
||||
|
||||
def get_line_number(block_token: BlockToken) -> int:
|
||||
return block_token.line_number # type: ignore
|
||||
|
||||
|
||||
def build_shard(
|
||||
start_line: int,
|
||||
end_line: int,
|
||||
markers: list[str] = [],
|
||||
tags: list[str] = [],
|
||||
children: list[Shard] = [],
|
||||
) -> Shard:
|
||||
if (
|
||||
len(children) == 1
|
||||
and len(tags) == 0
|
||||
and len(markers) == 0
|
||||
and children[0].start_line == start_line
|
||||
and children[0].end_line == end_line
|
||||
):
|
||||
return children[0]
|
||||
|
||||
return Shard(
|
||||
markers=markers,
|
||||
tags=tags,
|
||||
children=children,
|
||||
start_line=start_line,
|
||||
end_line=end_line,
|
||||
)
|
||||
|
||||
|
||||
def merge_into_first_shard(
|
||||
shards: list[Shard], start_line: int, end_line: int, additional_tags: list[str] = []
|
||||
):
|
||||
return shards[0].model_copy(
|
||||
update={
|
||||
"start_line": start_line,
|
||||
"end_line": end_line,
|
||||
"children": shards[1:],
|
||||
"tags": shards[0].tags + additional_tags,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def find_paragraph_shard_positions(block_tokens: list[BlockToken]) -> list[int]:
|
||||
return [
|
||||
index
|
||||
for index, block_token in enumerate(block_tokens)
|
||||
if isinstance(block_token, Paragraph) and has_markers(block_token)
|
||||
]
|
||||
|
||||
|
||||
def find_headings_by_level(
|
||||
block_tokens: list[BlockToken], header_level: int
|
||||
) -> list[int]:
|
||||
return [
|
||||
index
|
||||
for index, block_token in enumerate(block_tokens)
|
||||
if isinstance(block_token, Heading) and block_token.level == header_level
|
||||
]
|
||||
|
||||
|
||||
def calculate_heading_level_for_next_split(
|
||||
block_tokens: list[BlockToken],
|
||||
) -> int | None:
|
||||
"""
|
||||
If there is no marker in any heading, then return None.
|
||||
If only the first token is a heading with a marker, then return None.
|
||||
Otherwise: Return the heading level with the lowest level (h1 < h2), of which there are two or which has a marker (and doesn't stem from first)
|
||||
"""
|
||||
level_of_headings_without_first_with_marker = [
|
||||
token.level
|
||||
for token in block_tokens[1:]
|
||||
if isinstance(token, Heading) and has_markers(token)
|
||||
]
|
||||
|
||||
if len(level_of_headings_without_first_with_marker) == 0:
|
||||
return None
|
||||
|
||||
heading_level_counter = Counter(
|
||||
[token.level for token in block_tokens if isinstance(token, Heading)]
|
||||
)
|
||||
|
||||
return min(
|
||||
[level for level, count in heading_level_counter.items() if count >= 2]
|
||||
+ level_of_headings_without_first_with_marker
|
||||
)
|
||||
|
||||
|
||||
def parse_single_block_shards(
|
||||
block_token: BlockToken, start_line: int, end_line: int
|
||||
) -> tuple[Shard | None, list[str]]:
|
||||
markers, tags, children = [], [], []
|
||||
|
||||
if isinstance(block_token, List):
|
||||
list_items: list[ListItem] = ( # type: ignore
|
||||
list(block_token.children) if block_token.children is not None else []
|
||||
)
|
||||
for index, list_item in enumerate(list_items):
|
||||
list_item_start_line = get_line_number(list_item)
|
||||
list_item_end_line = (
|
||||
get_line_number(list_items[index + 1]) - 1
|
||||
if index + 1 < len(list_items)
|
||||
else end_line
|
||||
)
|
||||
list_item_shard, list_item_tags = parse_multiple_block_shards(
|
||||
list_item.children, # type: ignore
|
||||
list_item_start_line,
|
||||
list_item_end_line,
|
||||
)
|
||||
if list_item_shard is not None:
|
||||
children.append(list_item_shard)
|
||||
tags.extend(list_item_tags)
|
||||
|
||||
elif isinstance(block_token, (Paragraph, Heading)):
|
||||
markers, tags = extract_markers_and_tags(block_token)
|
||||
|
||||
if len(markers) == 0 and len(children) == 0:
|
||||
return None, tags
|
||||
|
||||
return build_shard(
|
||||
start_line, end_line, markers=markers, tags=tags, children=children
|
||||
), []
|
||||
|
||||
|
||||
def parse_multiple_block_shards(
|
||||
block_tokens: list[BlockToken],
|
||||
start_line: int,
|
||||
end_line: int,
|
||||
enforce_shard: bool = False,
|
||||
) -> tuple[Shard | None, list[str]]:
|
||||
is_first_block_heading = isinstance(block_tokens[0], Heading) and has_markers(
|
||||
block_tokens[0]
|
||||
)
|
||||
|
||||
paragraph_positions = find_paragraph_shard_positions(block_tokens)
|
||||
children, tags = [], []
|
||||
|
||||
is_first_block_only_with_marker = False
|
||||
|
||||
for i, token in enumerate(block_tokens):
|
||||
if i in paragraph_positions:
|
||||
is_first_block_only_with_marker = i == 0
|
||||
|
||||
child_start_line = get_line_number(token)
|
||||
child_end_line = (
|
||||
get_line_number(block_tokens[i + 1]) - 1
|
||||
if i + 1 < len(block_tokens)
|
||||
else end_line
|
||||
)
|
||||
|
||||
child_shard, child_tags = parse_single_block_shards(
|
||||
token, child_start_line, child_end_line
|
||||
)
|
||||
|
||||
if child_shard is not None:
|
||||
children.append(child_shard)
|
||||
if len(child_tags) > 0:
|
||||
tags.extend(child_tags)
|
||||
|
||||
if len(children) == 0 and not enforce_shard:
|
||||
return None, tags
|
||||
if is_first_block_heading or is_first_block_only_with_marker:
|
||||
return merge_into_first_shard(children, start_line, end_line, tags), []
|
||||
else:
|
||||
return build_shard(start_line, end_line, tags=tags, children=children), []
|
||||
|
||||
|
||||
def parse_header_shards(
|
||||
block_tokens: list[BlockToken],
|
||||
start_line: int,
|
||||
end_line: int,
|
||||
use_first_child_as_header: bool = False,
|
||||
) -> Shard | None:
|
||||
if len(block_tokens) == 0:
|
||||
return build_shard(start_line, end_line)
|
||||
|
||||
split_at_heading_level = calculate_heading_level_for_next_split(block_tokens)
|
||||
|
||||
if split_at_heading_level is None:
|
||||
return parse_multiple_block_shards(
|
||||
block_tokens, start_line, end_line, enforce_shard=True
|
||||
)[0]
|
||||
|
||||
heading_positions = find_headings_by_level(block_tokens, split_at_heading_level)
|
||||
|
||||
block_tokens_split_by_heading = split_at(block_tokens, heading_positions)
|
||||
|
||||
children = []
|
||||
for i, child_blocks in enumerate(block_tokens_split_by_heading):
|
||||
child_start_line = get_line_number(child_blocks[0])
|
||||
child_end_line = (
|
||||
get_line_number(block_tokens_split_by_heading[i + 1][0]) - 1
|
||||
if i + 1 < len(block_tokens_split_by_heading)
|
||||
else end_line
|
||||
)
|
||||
if child_shard := parse_header_shards(
|
||||
child_blocks,
|
||||
child_start_line,
|
||||
child_end_line,
|
||||
use_first_child_as_header=i > 0 or 0 in heading_positions,
|
||||
):
|
||||
children.append(child_shard)
|
||||
|
||||
if use_first_child_as_header and len(children) > 0:
|
||||
return merge_into_first_shard(children, start_line, end_line)
|
||||
else:
|
||||
return build_shard(start_line, end_line, children=children)
|
||||
|
||||
|
||||
def parse_markdown_file(file_name: str, file_content: str) -> StreamFile:
|
||||
shard = build_shard(1, max([len(file_content.splitlines()), 1]))
|
||||
|
||||
with TagMarkdownRenderer():
|
||||
ast = Document(file_content)
|
||||
|
||||
block_tokens: list[BlockToken] = ast.children # type: ignore
|
||||
if len(block_tokens) > 0:
|
||||
if parsed_shard := parse_header_shards(
|
||||
block_tokens, shard.start_line, shard.end_line
|
||||
):
|
||||
shard = parsed_shard
|
||||
|
||||
return StreamFile(shard=shard, file_name=file_name)
|
||||
|
||||
|
||||
__all__ = ["Shard", "StreamFile", "parse_markdown_file"]
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
from typing import Callable
|
||||
|
||||
from streamer.localize import LocalizedShard
|
||||
|
||||
|
||||
def find_shard(
|
||||
shards: list[LocalizedShard], query_function: Callable[[LocalizedShard], bool]
|
||||
) -> list[LocalizedShard]:
|
||||
found_shards = []
|
||||
|
||||
for shard in shards:
|
||||
if query_function(shard):
|
||||
found_shards.append(shard)
|
||||
found_shards.extend(find_shard(shard.children, query_function))
|
||||
|
||||
return found_shards
|
||||
|
||||
|
||||
def find_shard_by_position(
|
||||
shards: list[LocalizedShard], dimension: str, value: str
|
||||
) -> list[LocalizedShard]:
|
||||
return find_shard(
|
||||
shards,
|
||||
lambda shard: dimension in shard.location
|
||||
and shard.location[dimension] == value,
|
||||
)
|
||||
|
||||
|
||||
def find_shard_by_set_dimension(
|
||||
shards: list[LocalizedShard], dimension: str
|
||||
) -> list[LocalizedShard]:
|
||||
return find_shard(shards, lambda shard: dimension in shard.location)
|
||||
|
||||
|
||||
__all__ = ["find_shard_by_position", "find_shard", "find_shard_by_set_dimension"]
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
import os
|
||||
from pydantic_settings import (
|
||||
BaseSettings,
|
||||
PydanticBaseSettingsSource,
|
||||
SettingsConfigDict,
|
||||
YamlConfigSettingsSource,
|
||||
)
|
||||
from xdg_base_dirs import xdg_config_home
|
||||
|
||||
SETTINGS_FILE = xdg_config_home() / "streamer" / "config.yaml"
|
||||
|
||||
|
||||
class Settings(BaseSettings):
|
||||
model_config = SettingsConfigDict(env_file_encoding="utf-8")
|
||||
|
||||
base_folder: str = os.getcwd()
|
||||
|
||||
@classmethod
|
||||
def settings_customise_sources(
|
||||
cls,
|
||||
settings_cls: type[BaseSettings],
|
||||
init_settings: PydanticBaseSettingsSource,
|
||||
env_settings: PydanticBaseSettingsSource,
|
||||
dotenv_settings: PydanticBaseSettingsSource,
|
||||
file_secret_settings: PydanticBaseSettingsSource,
|
||||
) -> tuple[PydanticBaseSettingsSource, ...]:
|
||||
return (
|
||||
init_settings,
|
||||
YamlConfigSettingsSource(settings_cls, yaml_file=SETTINGS_FILE),
|
||||
dotenv_settings,
|
||||
env_settings,
|
||||
file_secret_settings,
|
||||
)
|
||||
76
uv.lock
generated
76
uv.lock
generated
|
|
@ -1,6 +1,6 @@
|
|||
version = 1
|
||||
revision = 3
|
||||
requires-python = ">=3.12"
|
||||
requires-python = ">=3.13"
|
||||
|
||||
[[package]]
|
||||
name = "annotated-doc"
|
||||
|
|
@ -20,6 +20,18 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "basedpyright"
|
||||
version = "1.38.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "nodejs-wheel-binaries" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/a2/d4/4ac6eeba6cfe2ad8586dcf87fdb9e8b045aa467b559bc2e24e91e84f58b2/basedpyright-1.38.0.tar.gz", hash = "sha256:7a9cf631d7eaf5859022a4352b51ed0e78ce115435a8599402239804000d0cdf", size = 25257385, upload-time = "2026-02-11T16:05:47.834Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/d5/90/1883cec16d667d944b08e8d8909b9b2f46cc1d2b9731e855e3c71f9b0450/basedpyright-1.38.0-py3-none-any.whl", hash = "sha256:a6c11a343fd12a2152a0d721b0e92f54f2e2e3322ee2562197e27dad952f1a61", size = 12303557, upload-time = "2026-02-11T16:05:44.863Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "click"
|
||||
version = "8.3.1"
|
||||
|
|
@ -93,12 +105,19 @@ wheels = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "nodeenv"
|
||||
version = "1.10.0"
|
||||
name = "nodejs-wheel-binaries"
|
||||
version = "24.13.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/24/bf/d1bda4f6168e0b2e9e5958945e01910052158313224ada5ce1fb2e1113b8/nodeenv-1.10.0.tar.gz", hash = "sha256:996c191ad80897d076bdfba80a41994c2b47c68e224c542b48feba42ba00f8bb", size = 55611, upload-time = "2025-12-20T14:08:54.006Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/e5/d0/81d98b8fddc45332f79d6ad5749b1c7409fb18723545eae75d9b7e0048fb/nodejs_wheel_binaries-24.13.1.tar.gz", hash = "sha256:512659a67449a038231e2e972d49e77049d2cf789ae27db39eff4ab1ca52ac57", size = 8056, upload-time = "2026-02-12T17:31:04.368Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl", hash = "sha256:5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827", size = 23438, upload-time = "2025-12-20T14:08:52.782Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/aa/04/1ffe1838306654fcb50bcf46172567d50c8e27a76f4b9e55a1971fab5c4f/nodejs_wheel_binaries-24.13.1-py2.py3-none-macosx_13_0_arm64.whl", hash = "sha256:360ac9382c651de294c23c4933a02358c4e11331294983f3cf50ca1ac32666b1", size = 54757440, upload-time = "2026-02-12T17:30:35.748Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/66/f6/81ad81bc3bd919a20b110130c4fd318c7b6a5abb37eb53daa353ad908012/nodejs_wheel_binaries-24.13.1-py2.py3-none-macosx_13_0_x86_64.whl", hash = "sha256:035b718946793986762cdd50deee7f5f1a8f1b0bad0f0cfd57cad5492f5ea018", size = 54932957, upload-time = "2026-02-12T17:30:40.114Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/14/be/8e8a2bd50953c4c5b7e0fca07368d287917b84054dc3c93dd26a2940f0f9/nodejs_wheel_binaries-24.13.1-py2.py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:f795e9238438c4225f76fbd01e2b8e1a322116bbd0dc15a7dbd585a3ad97961e", size = 59287257, upload-time = "2026-02-12T17:30:43.781Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/58/57/92f6dfa40647702a9fa6d32393ce4595d0fc03c1daa9b245df66cc60e959/nodejs_wheel_binaries-24.13.1-py2.py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:978328e3ad522571eb163b042dfbd7518187a13968fe372738f90fdfe8a46afc", size = 59781783, upload-time = "2026-02-12T17:30:47.387Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f7/a5/457b984cf675cf86ace7903204b9c36edf7a2d1b4325ddf71eaf8d1027c7/nodejs_wheel_binaries-24.13.1-py2.py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:e1dc893df85299420cd2a5feea0c3f8482a719b5f7f82d5977d58718b8b78b5f", size = 61287166, upload-time = "2026-02-12T17:30:50.646Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3c/99/da515f7bc3bce35cfa6005f0e0c4e3c4042a466782b143112eb393b663be/nodejs_wheel_binaries-24.13.1-py2.py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:0e581ae219a39073dcadd398a2eb648f0707b0f5d68c565586139f919c91cbe9", size = 61870142, upload-time = "2026-02-12T17:30:54.563Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cc/c0/22001d2c96d8200834af7d1de5e72daa3266c7270330275104c3d9ddd143/nodejs_wheel_binaries-24.13.1-py2.py3-none-win_amd64.whl", hash = "sha256:d4c969ea0bcb8c8b20bc6a7b4ad2796146d820278f17d4dc20229b088c833e22", size = 41185473, upload-time = "2026-02-12T17:30:57.524Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ab/c4/7532325f968ecfc078e8a028e69a52e4c3f95fb800906bf6931ac1e89e2b/nodejs_wheel_binaries-24.13.1-py2.py3-none-win_arm64.whl", hash = "sha256:caec398cb9e94c560bacdcba56b3828df22a355749eb291f47431af88cbf26dc", size = 38881194, upload-time = "2026-02-12T17:31:00.214Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -143,20 +162,6 @@ dependencies = [
|
|||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952, upload-time = "2025-11-04T13:43:49.098Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/5f/5d/5f6c63eebb5afee93bcaae4ce9a898f3373ca23df3ccaef086d0233a35a7/pydantic_core-2.41.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7", size = 2110990, upload-time = "2025-11-04T13:39:58.079Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/aa/32/9c2e8ccb57c01111e0fd091f236c7b371c1bccea0fa85247ac55b1e2b6b6/pydantic_core-2.41.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0", size = 1896003, upload-time = "2025-11-04T13:39:59.956Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/68/b8/a01b53cb0e59139fbc9e4fda3e9724ede8de279097179be4ff31f1abb65a/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69", size = 1919200, upload-time = "2025-11-04T13:40:02.241Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/38/de/8c36b5198a29bdaade07b5985e80a233a5ac27137846f3bc2d3b40a47360/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed2e99c456e3fadd05c991f8f437ef902e00eedf34320ba2b0842bd1c3ca3a75", size = 2052578, upload-time = "2025-11-04T13:40:04.401Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/00/b5/0e8e4b5b081eac6cb3dbb7e60a65907549a1ce035a724368c330112adfdd/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65840751b72fbfd82c3c640cff9284545342a4f1eb1586ad0636955b261b0b05", size = 2208504, upload-time = "2025-11-04T13:40:06.072Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/77/56/87a61aad59c7c5b9dc8caad5a41a5545cba3810c3e828708b3d7404f6cef/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e536c98a7626a98feb2d3eaf75944ef6f3dbee447e1f841eae16f2f0a72d8ddc", size = 2335816, upload-time = "2025-11-04T13:40:07.835Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0d/76/941cc9f73529988688a665a5c0ecff1112b3d95ab48f81db5f7606f522d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c", size = 2075366, upload-time = "2025-11-04T13:40:09.804Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d3/43/ebef01f69baa07a482844faaa0a591bad1ef129253ffd0cdaa9d8a7f72d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d38548150c39b74aeeb0ce8ee1d8e82696f4a4e16ddc6de7b1d8823f7de4b9b5", size = 2171698, upload-time = "2025-11-04T13:40:12.004Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b1/87/41f3202e4193e3bacfc2c065fab7706ebe81af46a83d3e27605029c1f5a6/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c23e27686783f60290e36827f9c626e63154b82b116d7fe9adba1fda36da706c", size = 2132603, upload-time = "2025-11-04T13:40:13.868Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/49/7d/4c00df99cb12070b6bccdef4a195255e6020a550d572768d92cc54dba91a/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:482c982f814460eabe1d3bb0adfdc583387bd4691ef00b90575ca0d2b6fe2294", size = 2329591, upload-time = "2025-11-04T13:40:15.672Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cc/6a/ebf4b1d65d458f3cda6a7335d141305dfa19bdc61140a884d165a8a1bbc7/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bfea2a5f0b4d8d43adf9d7b8bf019fb46fdd10a2e5cde477fbcb9d1fa08c68e1", size = 2319068, upload-time = "2025-11-04T13:40:17.532Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/49/3b/774f2b5cd4192d5ab75870ce4381fd89cf218af999515baf07e7206753f0/pydantic_core-2.41.5-cp312-cp312-win32.whl", hash = "sha256:b74557b16e390ec12dca509bce9264c3bbd128f8a2c376eaa68003d7f327276d", size = 1985908, upload-time = "2025-11-04T13:40:19.309Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/86/45/00173a033c801cacf67c190fef088789394feaf88a98a7035b0e40d53dc9/pydantic_core-2.41.5-cp312-cp312-win_amd64.whl", hash = "sha256:1962293292865bca8e54702b08a4f26da73adc83dd1fcf26fbc875b35d81c815", size = 2020145, upload-time = "2025-11-04T13:40:21.548Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f9/22/91fbc821fa6d261b376a3f73809f907cec5ca6025642c463d3488aad22fb/pydantic_core-2.41.5-cp312-cp312-win_arm64.whl", hash = "sha256:1746d4a3d9a794cacae06a5eaaccb4b8643a131d45fbc9af23e353dc0a5ba5c3", size = 1976179, upload-time = "2025-11-04T13:40:23.393Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/87/06/8806241ff1f70d9939f9af039c6c35f2360cf16e93c2ca76f184e76b1564/pydantic_core-2.41.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9", size = 2120403, upload-time = "2025-11-04T13:40:25.248Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/94/02/abfa0e0bda67faa65fef1c84971c7e45928e108fe24333c81f3bfe35d5f5/pydantic_core-2.41.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34", size = 1896206, upload-time = "2025-11-04T13:40:27.099Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/15/df/a4c740c0943e93e6500f9eb23f4ca7ec9bf71b19e608ae5b579678c8d02f/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0", size = 1919307, upload-time = "2025-11-04T13:40:29.806Z" },
|
||||
|
|
@ -199,10 +204,6 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/5c/96/5fb7d8c3c17bc8c62fdb031c47d77a1af698f1d7a406b0f79aaa1338f9ad/pydantic_core-2.41.5-cp314-cp314t-win32.whl", hash = "sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa", size = 1988906, upload-time = "2025-11-04T13:41:56.606Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/22/ed/182129d83032702912c2e2d8bbe33c036f342cc735737064668585dac28f/pydantic_core-2.41.5-cp314-cp314t-win_amd64.whl", hash = "sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c", size = 1981607, upload-time = "2025-11-04T13:41:58.889Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9f/ed/068e41660b832bb0b1aa5b58011dea2a3fe0ba7861ff38c4d4904c1c1a99/pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008", size = 1974769, upload-time = "2025-11-04T13:42:01.186Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/09/32/59b0c7e63e277fa7911c2fc70ccfb45ce4b98991e7ef37110663437005af/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd", size = 2110495, upload-time = "2025-11-04T13:42:49.689Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/aa/81/05e400037eaf55ad400bcd318c05bb345b57e708887f07ddb2d20e3f0e98/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc", size = 1915388, upload-time = "2025-11-04T13:42:52.215Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6e/0d/e3549b2399f71d56476b77dbf3cf8937cec5cd70536bdc0e374a421d0599/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56", size = 1942879, upload-time = "2025-11-04T13:42:56.483Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f7/07/34573da085946b6a313d7c42f82f16e8920bfd730665de2d11c0c37a74b5/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b", size = 2139017, upload-time = "2025-11-04T13:42:59.471Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -233,19 +234,6 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyright"
|
||||
version = "1.1.408"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "nodeenv" },
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/74/b2/5db700e52554b8f025faa9c3c624c59f1f6c8841ba81ab97641b54322f16/pyright-1.1.408.tar.gz", hash = "sha256:f28f2321f96852fa50b5829ea492f6adb0e6954568d1caa3f3af3a5f555eb684", size = 4400578, upload-time = "2026-01-08T08:07:38.795Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/0c/82/a2c93e32800940d9573fb28c346772a14778b84ba7524e691b324620ab89/pyright-1.1.408-py3-none-any.whl", hash = "sha256:090b32865f4fdb1e0e6cd82bf5618480d48eecd2eb2e70f960982a3d9a4c17c1", size = 6399144, upload-time = "2026-01-08T08:07:37.082Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pytest"
|
||||
version = "9.0.2"
|
||||
|
|
@ -277,16 +265,6 @@ version = "6.0.3"
|
|||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" },
|
||||
|
|
@ -365,7 +343,7 @@ wheels = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "streamer"
|
||||
name = "streamd"
|
||||
version = "0.1.0"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
|
|
@ -380,8 +358,8 @@ dependencies = [
|
|||
|
||||
[package.dev-dependencies]
|
||||
dev = [
|
||||
{ name = "basedpyright" },
|
||||
{ name = "faker" },
|
||||
{ name = "pyright" },
|
||||
{ name = "pytest" },
|
||||
{ name = "ruff" },
|
||||
]
|
||||
|
|
@ -399,8 +377,8 @@ requires-dist = [
|
|||
|
||||
[package.metadata.requires-dev]
|
||||
dev = [
|
||||
{ name = "basedpyright", specifier = "==1.38.0" },
|
||||
{ name = "faker", specifier = "==40.4.0" },
|
||||
{ name = "pyright", specifier = "==1.1.408" },
|
||||
{ name = "pytest", specifier = "==9.0.2" },
|
||||
{ name = "ruff", specifier = "==0.15.1" },
|
||||
]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue