diff --git a/.envrc b/.envrc index 99764ea..163bbd9 100644 --- a/.envrc +++ b/.envrc @@ -1 +1,3 @@ -use flake .#impure +#!/usr/bin/env bash +eval "$(devenv direnvrc)" +use devenv diff --git a/.forgejo/workflows/ci.yml b/.forgejo/workflows/ci.yml index 7fcb54b..6ce781f 100644 --- a/.forgejo/workflows/ci.yml +++ b/.forgejo/workflows/ci.yml @@ -5,7 +5,7 @@ on: workflow_dispatch: jobs: - build-and-lint: + check: name: Lint, Check & Test runs-on: nix @@ -16,8 +16,12 @@ jobs: - run: nix --version - run: nix flake check - - name: Install the project - run: 'nix develop .#impure --command bash -c "uv sync --locked --all-extras --dev"' + build: + name: Build Package + runs-on: nix - - name: Test with PyTest - run: nix develop .#impure --command bash -c "uv run pytest --junit-xml test-report.xml" + steps: + - name: Check out Repository + uses: https://git.konstantinfickel.de/actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + + - run: nix build diff --git a/.gitignore b/.gitignore index 2b9abaa..5976f3d 100644 --- a/.gitignore +++ b/.gitignore @@ -177,3 +177,8 @@ pyrightconfig.json .direnv test-report.xml +.devenv +.devenv.flake.nix +.pre-commit-config.yaml + +result diff --git a/.python-version b/.python-version index da71773..6324d40 100644 --- a/.python-version +++ b/.python-version @@ -1 +1 @@ -3.14.3 +3.14 diff --git a/README.md b/README.md index 2fcf603..9e5fa28 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,36 @@ -# streamer +# strea.md -Searching for `@tags` in time-based [streams](https://www.cs.yale.edu/homes/freeman/lifestreams.html). +![The Strea.md-Logo: A tag on an endless paper roll](./streamd.svg) -# Usage +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. -Running `streamer` finds all lines with @Task \ No newline at end of file +## Core Concepts + +- **Shards** — Sections of markdown files, organized hierarchically by headings. Each shard can contain markers, tags, and nested child shards. +- **Markers** — Special `@tags` like `@Task`, `@Done`, `@Waiting`, or `@Timesheet` that give shards semantic meaning and place them into dimensions. +- **Dimensions** — Classification axes (e.g. task state, project, timesheet) that categorize shards. Some dimensions propagate to child shards. + +## File Format + +Markdown files are named with a timestamp: `YYYYMMDD-HHMMSS [markers].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 + +- `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 + +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 `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 `streamd todo` finds all shards marked as open tasks and displays them as rich-formatted panels in your terminal. diff --git a/devenv.lock b/devenv.lock new file mode 100644 index 0000000..9996189 --- /dev/null +++ b/devenv.lock @@ -0,0 +1,123 @@ +{ + "nodes": { + "devenv": { + "locked": { + "dir": "src/modules", + "lastModified": 1771157881, + "owner": "cachix", + "repo": "devenv", + "rev": "b0b3dfa70ec90fa49f672e579f186faf4f61bd4b", + "type": "github" + }, + "original": { + "dir": "src/modules", + "owner": "cachix", + "repo": "devenv", + "type": "github" + } + }, + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1767039857, + "owner": "NixOS", + "repo": "flake-compat", + "rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab", + "type": "github" + }, + "original": { + "owner": "NixOS", + "repo": "flake-compat", + "type": "github" + } + }, + "git-hooks": { + "inputs": { + "flake-compat": "flake-compat", + "gitignore": "gitignore", + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1770726378, + "owner": "cachix", + "repo": "git-hooks.nix", + "rev": "5eaaedde414f6eb1aea8b8525c466dc37bba95ae", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "git-hooks.nix", + "type": "github" + } + }, + "gitignore": { + "inputs": { + "nixpkgs": [ + "git-hooks", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1762808025, + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "cb5e3fdca1de58ccbc3ef53de65bd372b48f567c", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, + "nixpkgs": { + "inputs": { + "nixpkgs-src": "nixpkgs-src" + }, + "locked": { + "lastModified": 1770434727, + "owner": "cachix", + "repo": "devenv-nixpkgs", + "rev": "8430f16a39c27bdeef236f1eeb56f0b51b33d348", + "type": "github" + }, + "original": { + "owner": "cachix", + "ref": "rolling", + "repo": "devenv-nixpkgs", + "type": "github" + } + }, + "nixpkgs-src": { + "flake": false, + "locked": { + "lastModified": 1769922788, + "narHash": "sha256-H3AfG4ObMDTkTJYkd8cz1/RbY9LatN5Mk4UF48VuSXc=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "207d15f1a6603226e1e223dc79ac29c7846da32e", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "devenv": "devenv", + "git-hooks": "git-hooks", + "nixpkgs": "nixpkgs", + "pre-commit-hooks": [ + "git-hooks" + ] + } + } + }, + "root": "root", + "version": 7 +} diff --git a/devenv.nix b/devenv.nix new file mode 100644 index 0000000..ce1097e --- /dev/null +++ b/devenv.nix @@ -0,0 +1,28 @@ +{ + pkgs, + ... +}: +{ + languages = { + python = { + enable = true; + uv.enable = true; + }; + }; + + packages = [ + pkgs.basedpyright + ]; + + git-hooks.hooks = { + basedpyright = { + enable = true; + entry = "${pkgs.basedpyright}/bin/basedpyright"; + files = "\\.py$"; + types = [ "file" ]; + }; + ruff.enable = true; + ruff-format.enable = true; + commitizen.enable = true; + }; +} diff --git a/devenv.yaml b/devenv.yaml new file mode 100644 index 0000000..28877ba --- /dev/null +++ b/devenv.yaml @@ -0,0 +1,6 @@ +inputs: + git-hooks: + url: github:cachix/git-hooks.nix + inputs: + nixpkgs: + follows: nixpkgs diff --git a/flake.lock b/flake.lock index 0f2727f..b04d8e3 100644 --- a/flake.lock +++ b/flake.lock @@ -1,16 +1,75 @@ { "nodes": { - "nixpkgs": { + "flake-compat": { + "flake": false, "locked": { - "lastModified": 1769789167, - "narHash": "sha256-kKB3bqYJU5nzYeIROI82Ef9VtTbu4uA3YydSk/Bioa8=", + "lastModified": 1767039857, + "narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=", "owner": "NixOS", - "repo": "nixpkgs", - "rev": "62c8382960464ceb98ea593cb8321a2cf8f9e3e5", + "repo": "flake-compat", + "rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab", "type": "github" }, "original": { "owner": "NixOS", + "repo": "flake-compat", + "type": "github" + } + }, + "git-hooks": { + "inputs": { + "flake-compat": "flake-compat", + "gitignore": "gitignore", + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1770726378, + "narHash": "sha256-kck+vIbGOaM/dHea7aTBxdFYpeUl/jHOy5W3eyRvVx8=", + "owner": "cachix", + "repo": "git-hooks.nix", + "rev": "5eaaedde414f6eb1aea8b8525c466dc37bba95ae", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "git-hooks.nix", + "type": "github" + } + }, + "gitignore": { + "inputs": { + "nixpkgs": [ + "git-hooks", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1709087332, + "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1771008912, + "narHash": "sha256-gf2AmWVTs8lEq7z/3ZAsgnZDhWIckkb+ZnAo5RzSxJg=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "a82ccc39b39b621151d6732718e3e250109076fa", + "type": "github" + }, + "original": { + "owner": "nixos", "ref": "nixos-unstable", "repo": "nixpkgs", "type": "github" @@ -29,11 +88,11 @@ ] }, "locked": { - "lastModified": 1763662255, - "narHash": "sha256-4bocaOyLa3AfiS8KrWjZQYu+IAta05u3gYZzZ6zXbT0=", + "lastModified": 1771039651, + "narHash": "sha256-WZOfX4APbc6vmL14ZWJXgBeRfEER8H+OIX0D0nSmv0M=", "owner": "pyproject-nix", "repo": "build-system-pkgs", - "rev": "042904167604c681a090c07eb6967b4dd4dae88c", + "rev": "69bc2b53b79cbd6ce9f66f506fc962b45b5e68b9", "type": "github" }, "original": { @@ -64,6 +123,7 @@ }, "root": { "inputs": { + "git-hooks": "git-hooks", "nixpkgs": "nixpkgs", "pyproject-build-systems": "pyproject-build-systems", "pyproject-nix": "pyproject-nix", @@ -80,11 +140,11 @@ ] }, "locked": { - "lastModified": 1769957392, - "narHash": "sha256-6PkqwwYf5K2CHi2V+faI/9pqjfz/HxUkI/MVid6hlOY=", + "lastModified": 1770770348, + "narHash": "sha256-A2GzkmzdYvdgmMEu5yxW+xhossP+txrYb7RuzRaqhlg=", "owner": "pyproject-nix", "repo": "uv2nix", - "rev": "d18bc50ae1c3d4be9c41c2d94ea765524400af75", + "rev": "5d1b2cb4fe3158043fbafbbe2e46238abbc954b0", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 669701a..32093d0 100644 --- a/flake.nix +++ b/flake.nix @@ -1,8 +1,8 @@ { - description = "Hello world flake using uv2nix"; + description = "Using Markdown Files to organize your life as a @Tag-Stream"; inputs = { - nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; pyproject-nix = { url = "github:pyproject-nix/pyproject.nix"; @@ -21,204 +21,215 @@ inputs.uv2nix.follows = "uv2nix"; inputs.nixpkgs.follows = "nixpkgs"; }; + + git-hooks = { + url = "github:cachix/git-hooks.nix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; }; outputs = { self, nixpkgs, - uv2nix, pyproject-nix, + uv2nix, pyproject-build-systems, + git-hooks, ... }: let inherit (nixpkgs) lib; + forAllSystems = lib.genAttrs lib.systems.flakeExposed; - # Load a uv workspace from a workspace root. - # Uv2nix treats all uv projects as workspace projects. workspace = uv2nix.lib.workspace.loadWorkspace { workspaceRoot = ./.; }; - # Create package overlay from workspace. overlay = workspace.mkPyprojectOverlay { - # Prefer prebuilt binary wheels as a package source. - # Sdists are less likely to "just work" because of the metadata missing from uv.lock. - # Binary wheels are more likely to, but may still require overrides for library dependencies. - sourcePreference = "wheel"; # or sourcePreference = "sdist"; - # Optionally customise PEP 508 environment - # environ = { - # platform_release = "5.10.65"; - # }; + sourcePreference = "wheel"; }; - # Extend generated overlay with build fixups - # - # Uv2nix can only work with what it has, and uv.lock is missing essential metadata to perform some builds. - # This is an additional overlay implementing build fixups. - # See: - # - https://pyproject-nix.github.io/uv2nix/FAQ.html - pyprojectOverrides = _final: _prev: { - # Implement build fixups here. - # Note that uv2nix is _not_ using Nixpkgs buildPythonPackage. - # It's using https://pyproject-nix.github.io/pyproject.nix/build.html + editableOverlay = workspace.mkEditablePyprojectOverlay { + root = "$REPO_ROOT"; }; - # This example is only using x86_64-linux - pkgs = nixpkgs.legacyPackages.x86_64-linux; + pythonSets = forAllSystems ( + system: + let + pkgs = nixpkgs.legacyPackages.${system}; + inherit (pkgs) stdenv; - # Use Python 3.14 from nixpkgs - python = pkgs.python314; + baseSet = pkgs.callPackage pyproject-nix.build.packages { + python = pkgs.python313; + }; - # Construct package set - pythonSet = - # Use base package set from pyproject.nix builders - (pkgs.callPackage pyproject-nix.build.packages { - inherit python; - }).overrideScope - ( - lib.composeManyExtensions [ - pyproject-build-systems.overlays.default - overlay - pyprojectOverrides - ] - ); + pyprojectOverrides = _final: prev: { + streamd = prev.streamd.overrideAttrs (old: { + passthru = old.passthru // { + tests = (old.passthru.tests or { }) // { + pytest = stdenv.mkDerivation { + name = "${_final.streamd.name}-pytest"; + inherit (_final.streamd) src; + nativeBuildInputs = [ + (_final.mkVirtualEnv "streamd-pytest-env" { + streamd = [ "dev" ]; + }) + ]; + dontConfigure = true; + buildPhase = '' + runHook preBuild + # Exit code 5 means no tests collected — allow it so the + # check succeeds on an empty test suite. + pytest || [ $? -eq 5 ] + runHook postBuild + ''; + installPhase = '' + runHook preInstall + touch $out + runHook postInstall + ''; + }; + }; + }; + }); + }; + + in + baseSet.overrideScope ( + lib.composeManyExtensions [ + pyproject-build-systems.overlays.default + overlay + pyprojectOverrides + ] + ) + ); + + mkGitHooksCheck = + system: + let + pkgs = nixpkgs.legacyPackages.${system}; + pythonSet = pythonSets.${system}; + venv = pythonSet.mkVirtualEnv "streamd-check-env" workspace.deps.all; + in + git-hooks.lib.${system}.run { + src = ./.; + hooks = { + basedpyright = { + enable = true; + entry = "${pkgs.basedpyright}/bin/basedpyright --pythonpath ${venv}/bin/python --project ${ + pkgs.writeText "pyrightconfig.json" ( + builtins.toJSON { + reportMissingTypeStubs = false; + reportUnnecessaryTypeIgnoreComment = false; + } + ) + }"; + files = "\\.py$"; + types = [ "file" ]; + }; + ruff.enable = true; + ruff-format.enable = true; + commitizen.enable = true; + }; + }; in { - # Package a virtual environment as our main application. - # - # Enable no optional dependencies for production build. - packages.x86_64-linux = + packages = forAllSystems ( + system: let - streamer = pythonSet.mkVirtualEnv "streamer-env" workspace.deps.default; + pythonSet = pythonSets.${system}; + pkgs = nixpkgs.legacyPackages.${system}; + inherit (pkgs.callPackages pyproject-nix.build.util { }) mkApplication; + in + rec { + streamd = mkApplication { + venv = pythonSet.mkVirtualEnv "streamd-env" workspace.deps.default; + package = pythonSet.streamd; + }; + default = streamd; + } + ); + + homeManagerModules.default = + { + lib, + config, + pkgs, + ... + }: + let + cfg = config.programs.streamd; in { - inherit streamer; - default = streamer; - }; + options.programs.streamd = { + enable = lib.mkEnableOption "streamd"; - # Make streamer runnable with `nix run` - apps.x86_64-linux = { - default = { - type = "app"; - program = "${self.packages.x86_64-linux.default}/bin/streamer"; - }; - }; - - # This example provides two different modes of development: - # - Impurely using uv to manage virtual environments - # - Pure development using uv2nix to manage virtual environments - devShells.x86_64-linux = { - # It is of course perfectly OK to keep using an impure virtualenv workflow and only use uv2nix to build packages. - # This devShell simply adds Python and undoes the dependency leakage done by Nixpkgs Python infrastructure. - impure = pkgs.mkShell { - packages = with pkgs; [ - python - uv - pre-commit - bashInteractive - ]; - env = { - # Prevent uv from managing Python downloads - UV_PYTHON_DOWNLOADS = "never"; - # Force uv to use nixpkgs Python interpreter - UV_PYTHON = python.interpreter; - } - // lib.optionalAttrs pkgs.stdenv.isLinux { - # Python libraries often load native shared objects using dlopen(3). - # Setting LD_LIBRARY_PATH makes the dynamic library loader aware of libraries without using RPATH for lookup. - LD_LIBRARY_PATH = lib.makeLibraryPath pkgs.pythonManylinuxPackages.manylinux1; - }; - shellHook = '' - unset PYTHONPATH - ''; - }; - - # This devShell uses uv2nix to construct a virtual environment purely from Nix, using the same dependency specification as the application. - # The notable difference is that we also apply another overlay here enabling editable mode ( https://setuptools.pypa.io/en/latest/userguide/development_mode.html ). - # - # This means that any changes done to your local files do not require a rebuild. - # - # Note: Editable package support is still unstable and subject to change. - uv2nix = - let - # Create an overlay enabling editable mode for all local dependencies. - editableOverlay = workspace.mkEditablePyprojectOverlay { - # Use environment variable - root = "$REPO_ROOT"; - # Optional: Only enable editable for these packages - # members = [ "streamer" ]; + base-folder = lib.mkOption { + type = lib.types.str; + description = "Base Folder of streamd"; }; - # Override previous set with our overrideable overlay. - editablePythonSet = pythonSet.overrideScope ( - lib.composeManyExtensions [ - editableOverlay + package = lib.mkOption { + type = lib.types.package; + default = self.packages.${pkgs.system}.streamd; + defaultText = lib.literalExpression "inputs.streamd.packages.\${pkgs.system}.streamd"; + description = "The package to use for the streamd binary."; + }; + }; - # Apply fixups for building an editable package of your workspace packages - (final: prev: { - streamer = prev.streamer.overrideAttrs (old: { - # It's a good idea to filter the sources going into an editable build - # so the editable package doesn't have to be rebuilt on every change. - src = lib.fileset.toSource { - root = old.src; - fileset = lib.fileset.unions [ - (old.src + "/pyproject.toml") - (old.src + "/README.md") - (old.src + "/src/streamer/__init__.py") - ]; - }; - - # Hatchling (our build system) has a dependency on the `editables` package when building editables. - # - # In normal Python flows this dependency is dynamically handled, and doesn't need to be explicitly declared. - # This behaviour is documented in PEP-660. - # - # With Nix the dependency needs to be explicitly declared. - nativeBuildInputs = - old.nativeBuildInputs - ++ final.resolveBuildSystem { - editables = [ ]; - }; - }); - - }) - ] - ); - - # Build virtual environment, with local packages being editable. - # - # Enable all optional dependencies for development. - virtualenv = editablePythonSet.mkVirtualEnv "streamer-dev-env" workspace.deps.all; - - in - pkgs.mkShell { - packages = with pkgs; [ - virtualenv - uv - pre-commit - bashInteractive + config = lib.mkIf cfg.enable { + assertions = [ + (lib.hm.assertions.assertPlatform "programs.streamd" pkgs lib.platforms.linux) ]; + home.packages = [ cfg.package ]; + + xdg.configFile."streamd/config.yaml".source = + (pkgs.formats.yaml { }).generate "streamd-configuration" + { + base_folder = cfg.base-folder; + }; + + home.shellAliases.s = "streamd"; + }; + }; + + checks = forAllSystems ( + system: + let + pythonSet = pythonSets.${system}; + in + { + inherit (pythonSet.streamd.passthru.tests) pytest; + pre-commit = mkGitHooksCheck system; + } + ); + + devShells = forAllSystems ( + system: + let + pkgs = nixpkgs.legacyPackages.${system}; + pythonSet = pythonSets.${system}.overrideScope editableOverlay; + virtualenv = pythonSet.mkVirtualEnv "streamd-dev-env" workspace.deps.all; + in + { + default = pkgs.mkShell { + packages = [ + virtualenv + pkgs.uv + ]; env = { - # Don't create venv using uv UV_NO_SYNC = "1"; - - # Force uv to use Python interpreter from venv - UV_PYTHON = "${virtualenv}/bin/python"; - - # Prevent uv from downloading managed Python's + UV_PYTHON = pythonSet.python.interpreter; UV_PYTHON_DOWNLOADS = "never"; }; - shellHook = '' - # Undo dependency propagation by nixpkgs. unset PYTHONPATH - - # Get repository root using git. This is expanded at runtime by the editable `.pth` machinery. export REPO_ROOT=$(git rev-parse --show-toplevel) + ${(mkGitHooksCheck system).shellHook} ''; }; - }; + } + ); }; } diff --git a/pyproject.toml b/pyproject.toml index a58de8c..0dc712d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,21 +1,21 @@ [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", "pydantic==2.12.5", - "pydantic-settings[yaml]==2.12.0", - "rich==14.3.2", - "typer==0.21.2", + "pydantic-settings[yaml]==2.13.1", + "rich==14.3.3", + "typer==0.24.1", "xdg-base-dirs==6.0.2", ] [project.scripts] -streamer = "streamer:app" +streamd = "streamd:app" [build-system] requires = ["hatchling"] @@ -23,8 +23,8 @@ build-backend = "hatchling.build" [dependency-groups] dev = [ - "faker==40.4.0", - "pyright==1.1.408", + "basedpyright==1.38.2", + "faker==40.11.0", "pytest==9.0.2", - "ruff==0.15.0", + "ruff==0.15.6", ] diff --git a/src/streamer/__init__.py b/src/streamd/__init__.py similarity index 82% rename from src/streamer/__init__.py rename to src/streamd/__init__.py index 6109ca8..c72d098 100644 --- a/src/streamer/__init__.py +++ b/src/streamd/__init__.py @@ -1,8 +1,9 @@ import glob import os +from collections.abc import Generator from datetime import datetime from shutil import move -from typing import Annotated, Generator +from typing import Annotated import click import typer @@ -10,18 +11,17 @@ from rich import print from rich.markdown import Markdown from rich.panel import Panel -from streamer.localize import ( +from streamd.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 +from streamd.localize.preconfigured_configurations import TaskConfiguration +from streamd.parse import parse_markdown_file +from streamd.query import find_shard_by_position +from streamd.settings import Settings +from streamd.timesheet.configuration import BasicTimesheetConfiguration +from streamd.timesheet.extract import extract_timesheets app = typer.Typer() @@ -89,11 +89,11 @@ def timesheet() -> None: @app.command() def new() -> None: - streamer_directory = Settings().base_folder + streamd_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) + prelimary_path = os.path.join(streamd_directory, preliminary_file_name) content = "# " with open(prelimary_path, "w") as file: @@ -111,7 +111,7 @@ def new() -> None: ): final_file_name = f"{timestamp} {' '.join(markers)}.md" - final_path = os.path.join(streamer_directory, final_file_name) + final_path = os.path.join(streamd_directory, final_file_name) _ = move(prelimary_path, final_path) print(f"Saved as [yellow]{final_file_name}") diff --git a/src/streamer/localize/__init__.py b/src/streamd/localize/__init__.py similarity index 100% rename from src/streamer/localize/__init__.py rename to src/streamd/localize/__init__.py diff --git a/src/streamer/localize/extract_datetime.py b/src/streamd/localize/extract_datetime.py similarity index 100% rename from src/streamer/localize/extract_datetime.py rename to src/streamd/localize/extract_datetime.py diff --git a/src/streamer/localize/localize.py b/src/streamd/localize/localize.py similarity index 92% rename from src/streamer/localize/localize.py rename to src/streamd/localize/localize.py index b241fc9..bc084f4 100644 --- a/src/streamer/localize/localize.py +++ b/src/streamd/localize/localize.py @@ -1,6 +1,6 @@ from datetime import datetime -from streamer.parse.shard import Shard, StreamFile +from streamd.parse.shard import Shard, StreamFile from .extract_datetime import ( extract_datetime_from_file_name, @@ -47,7 +47,10 @@ def localize_shard( position.update(private_position) return LocalizedShard( - **shard.model_dump(exclude={"children"}), + markers=shard.markers, + tags=shard.tags, + start_line=shard.start_line, + end_line=shard.end_line, location=position, children=children, moment=adjusted_moment, diff --git a/src/streamer/localize/localized_shard.py b/src/streamd/localize/localized_shard.py similarity index 87% rename from src/streamer/localize/localized_shard.py rename to src/streamd/localize/localized_shard.py index 05cb016..871af20 100644 --- a/src/streamer/localize/localized_shard.py +++ b/src/streamd/localize/localized_shard.py @@ -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): diff --git a/src/streamer/localize/preconfigured_configurations.py b/src/streamd/localize/preconfigured_configurations.py similarity index 95% rename from src/streamer/localize/preconfigured_configurations.py rename to src/streamd/localize/preconfigured_configurations.py index 0da82d7..a949ddc 100644 --- a/src/streamer/localize/preconfigured_configurations.py +++ b/src/streamd/localize/preconfigured_configurations.py @@ -1,4 +1,4 @@ -from streamer.localize.repository_configuration import ( +from streamd.localize.repository_configuration import ( Dimension, Marker, MarkerPlacement, diff --git a/src/streamer/localize/repository_configuration.py b/src/streamd/localize/repository_configuration.py similarity index 95% rename from src/streamer/localize/repository_configuration.py rename to src/streamd/localize/repository_configuration.py index f21c556..4b83a03 100644 --- a/src/streamer/localize/repository_configuration.py +++ b/src/streamd/localize/repository_configuration.py @@ -1,13 +1,11 @@ from __future__ import annotations -from typing import Optional - from pydantic import BaseModel class Dimension(BaseModel): display_name: str - comment: Optional[str] = None + comment: str | None = None propagate: bool = False @@ -29,7 +27,7 @@ class RepositoryConfiguration(BaseModel): def merge_single_dimension(base: Dimension, second: Dimension) -> Dimension: - second_fields_set = getattr(second, "model_fields_set", set()) + second_fields_set: set[str] = getattr(second, "model_fields_set", set()) return Dimension( display_name=second.display_name or base.display_name, diff --git a/src/streamer/parse/__init__.py b/src/streamd/parse/__init__.py similarity index 100% rename from src/streamer/parse/__init__.py rename to src/streamd/parse/__init__.py diff --git a/src/streamer/parse/extract_tag.py b/src/streamd/parse/extract_tag.py similarity index 72% rename from src/streamer/parse/extract_tag.py rename to src/streamd/parse/extract_tag.py index ace1258..b7bfd45 100644 --- a/src/streamer/parse/extract_tag.py +++ b/src/streamd/parse/extract_tag.py @@ -1,7 +1,9 @@ import re -from typing import Iterable +from collections.abc import Iterable +from typing import cast + from mistletoe.block_token import BlockToken -from mistletoe.span_token import Emphasis, RawText, Strikethrough, Strong, Link +from mistletoe.span_token import Emphasis, Link, RawText, Strikethrough, Strong from mistletoe.token import Token from .markdown_tag import Tag @@ -12,18 +14,21 @@ def extract_markers_and_tags_from_single_token( marker_boundary_encountered: bool, return_at_first_marker: bool = False, ) -> tuple[list[str], list[str], bool]: - result_markers, result_tags = [], [] + result_markers: list[str] = [] + result_tags: list[str] = [] result_marker_boundary_encountered = marker_boundary_encountered if isinstance(token, Tag): + content = cast(str, token.content) if marker_boundary_encountered: - result_tags.append(token.content) + result_tags.append(content) else: - result_markers.append(token.content) + result_markers.append(content) elif isinstance(token, (Emphasis, Strong, Strikethrough, Link)): + children = list(token.children or []) markers, tags, child_marker_boundary_encountered = ( extract_markers_and_tags_from_tokens( - token.children or [], + children, marker_boundary_encountered, return_at_first_marker, ) @@ -33,8 +38,10 @@ def extract_markers_and_tags_from_single_token( result_marker_boundary_encountered = ( marker_boundary_encountered or child_marker_boundary_encountered ) - elif isinstance(token, RawText) and re.match(r"^[\s]*$", token.content): - pass + elif isinstance(token, RawText): + content_raw = cast(str, token.content) + if not re.match(r"^[\s]*$", content_raw): + result_marker_boundary_encountered = True else: result_marker_boundary_encountered = True @@ -46,7 +53,8 @@ def extract_markers_and_tags_from_tokens( marker_boundary_encountered: bool, return_at_first_marker: bool = False, ) -> tuple[list[str], list[str], bool]: - result_markers, result_tags = [], [] + result_markers: list[str] = [] + result_tags: list[str] = [] result_marker_boundary_encountered = marker_boundary_encountered for child in tokens: @@ -68,15 +76,15 @@ def extract_markers_and_tags_from_tokens( 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 - ) + children = list(block_token.children or []) + markers, tags, _ = extract_markers_and_tags_from_tokens(children, False) return markers, tags def has_markers(block_token: BlockToken) -> bool: + children = list(block_token.children or []) markers, _, _ = extract_markers_and_tags_from_tokens( - block_token.children or [], False, return_at_first_marker=True + children, False, return_at_first_marker=True ) return len(markers) > 0 diff --git a/src/streamer/parse/list.py b/src/streamd/parse/list.py similarity index 100% rename from src/streamer/parse/list.py rename to src/streamd/parse/list.py diff --git a/src/streamd/parse/markdown_tag.py b/src/streamd/parse/markdown_tag.py new file mode 100644 index 0000000..798f10e --- /dev/null +++ b/src/streamd/parse/markdown_tag.py @@ -0,0 +1,23 @@ +import re +from typing import cast + +from mistletoe.markdown_renderer import Fragment, MarkdownRenderer +from mistletoe.span_token import SpanToken + + +class Tag(SpanToken): + parse_inner: bool = False + pattern: re.Pattern[str] = re.compile(r"@([^\s*\x60~\[\]]+)") + + +class TagMarkdownRenderer(MarkdownRenderer): + def __init__(self) -> None: + super().__init__(Tag) # pyright: ignore[reportUnknownMemberType] + + def render_tag(self, token: Tag): + content = cast(str, token.content) + yield Fragment("@") + yield Fragment(content) + + +__all__ = ["Tag", "TagMarkdownRenderer"] diff --git a/src/streamer/parse/parse.py b/src/streamd/parse/parse.py similarity index 83% rename from src/streamer/parse/parse.py rename to src/streamd/parse/parse.py index 058912a..4d14fa3 100644 --- a/src/streamer/parse/parse.py +++ b/src/streamd/parse/parse.py @@ -1,4 +1,5 @@ from collections import Counter +from typing import cast from mistletoe.block_token import ( BlockToken, @@ -16,16 +17,20 @@ from .shard import Shard, StreamFile def get_line_number(block_token: BlockToken) -> int: - return block_token.line_number # type: ignore + return cast(int, block_token.line_number) # pyright: ignore[reportAttributeAccessIssue] def build_shard( start_line: int, end_line: int, - markers: list[str] = [], - tags: list[str] = [], - children: list[Shard] = [], + markers: list[str] | None = None, + tags: list[str] | None = None, + children: list[Shard] | None = None, ) -> Shard: + markers = markers or [] + tags = tags or [] + children = children or [] + if ( len(children) == 1 and len(tags) == 0 @@ -45,14 +50,17 @@ def build_shard( def merge_into_first_shard( - shards: list[Shard], start_line: int, end_line: int, additional_tags: list[str] = [] -): + shards: list[Shard], + start_line: int, + end_line: int, + additional_tags: list[str] | None = None, +) -> Shard: return shards[0].model_copy( update={ "start_line": start_line, "end_line": end_line, "children": shards[1:], - "tags": shards[0].tags + additional_tags, + "tags": shards[0].tags + (additional_tags or []), } ) @@ -65,13 +73,18 @@ def find_paragraph_shard_positions(block_tokens: list[BlockToken]) -> list[int]: ] +def _heading_level(heading: Heading) -> int: + return cast(int, heading.level) + + 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 + if isinstance(block_token, Heading) + and _heading_level(block_token) == header_level ] @@ -83,8 +96,8 @@ def calculate_heading_level_for_next_split( 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 + level_of_headings_without_first_with_marker: list[int] = [ + _heading_level(token) for token in block_tokens[1:] if isinstance(token, Heading) and has_markers(token) ] @@ -92,8 +105,8 @@ def calculate_heading_level_for_next_split( 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)] + heading_level_counter: Counter[int] = Counter( + [_heading_level(token) for token in block_tokens if isinstance(token, Heading)] ) return min( @@ -105,10 +118,12 @@ def calculate_heading_level_for_next_split( def parse_single_block_shards( block_token: BlockToken, start_line: int, end_line: int ) -> tuple[Shard | None, list[str]]: - markers, tags, children = [], [], [] + markers: list[str] = [] + tags: list[str] = [] + children: list[Shard] = [] if isinstance(block_token, List): - list_items: list[ListItem] = ( # type: ignore + list_items: list[ListItem] = ( # pyright: ignore[reportAssignmentType] list(block_token.children) if block_token.children is not None else [] ) for index, list_item in enumerate(list_items): @@ -119,7 +134,7 @@ def parse_single_block_shards( else end_line ) list_item_shard, list_item_tags = parse_multiple_block_shards( - list_item.children, # type: ignore + list_item.children, # pyright: ignore[reportArgumentType] list_item_start_line, list_item_end_line, ) @@ -149,7 +164,8 @@ def parse_multiple_block_shards( ) paragraph_positions = find_paragraph_shard_positions(block_tokens) - children, tags = [], [] + children: list[Shard] = [] + tags: list[str] = [] is_first_block_only_with_marker = False @@ -201,7 +217,7 @@ def parse_header_shards( block_tokens_split_by_heading = split_at(block_tokens, heading_positions) - children = [] + children: list[Shard] = [] for i, child_blocks in enumerate(block_tokens_split_by_heading): child_start_line = get_line_number(child_blocks[0]) child_end_line = ( @@ -229,7 +245,7 @@ def parse_markdown_file(file_name: str, file_content: str) -> StreamFile: with TagMarkdownRenderer(): ast = Document(file_content) - block_tokens: list[BlockToken] = ast.children # type: ignore + block_tokens: list[BlockToken] = ast.children # pyright: ignore[reportAssignmentType] if len(block_tokens) > 0: if parsed_shard := parse_header_shards( block_tokens, shard.start_line, shard.end_line diff --git a/src/streamer/parse/shard.py b/src/streamd/parse/shard.py similarity index 100% rename from src/streamer/parse/shard.py rename to src/streamd/parse/shard.py diff --git a/src/streamer/query/__init__.py b/src/streamd/query/__init__.py similarity index 100% rename from src/streamer/query/__init__.py rename to src/streamd/query/__init__.py diff --git a/src/streamer/query/find.py b/src/streamd/query/find.py similarity index 79% rename from src/streamer/query/find.py rename to src/streamd/query/find.py index 49beeca..428e05a 100644 --- a/src/streamer/query/find.py +++ b/src/streamd/query/find.py @@ -1,12 +1,12 @@ from typing import Callable -from streamer.localize import LocalizedShard +from streamd.localize import LocalizedShard def find_shard( shards: list[LocalizedShard], query_function: Callable[[LocalizedShard], bool] ) -> list[LocalizedShard]: - found_shards = [] + found_shards: list[LocalizedShard] = [] for shard in shards: if query_function(shard): @@ -21,8 +21,9 @@ def find_shard_by_position( ) -> list[LocalizedShard]: return find_shard( shards, - lambda shard: dimension in shard.location - and shard.location[dimension] == value, + lambda shard: ( + dimension in shard.location and shard.location[dimension] == value + ), ) diff --git a/src/streamer/settings/__init__.py b/src/streamd/settings/__init__.py similarity index 79% rename from src/streamer/settings/__init__.py rename to src/streamd/settings/__init__.py index 8099b01..bdf28b4 100644 --- a/src/streamer/settings/__init__.py +++ b/src/streamd/settings/__init__.py @@ -1,4 +1,6 @@ import os +from typing import ClassVar, override + from pydantic_settings import ( BaseSettings, PydanticBaseSettingsSource, @@ -7,15 +9,18 @@ from pydantic_settings import ( ) from xdg_base_dirs import xdg_config_home -SETTINGS_FILE = xdg_config_home() / "streamer" / "config.yaml" +SETTINGS_FILE = xdg_config_home() / "streamd" / "config.yaml" class Settings(BaseSettings): - model_config = SettingsConfigDict(env_file_encoding="utf-8") + model_config: ClassVar[SettingsConfigDict] = SettingsConfigDict( + env_file_encoding="utf-8" + ) base_folder: str = os.getcwd() @classmethod + @override def settings_customise_sources( cls, settings_cls: type[BaseSettings], diff --git a/src/streamer/timesheet/configuration.py b/src/streamd/timesheet/configuration.py similarity index 96% rename from src/streamer/timesheet/configuration.py rename to src/streamd/timesheet/configuration.py index 6b6c55d..8453dc9 100644 --- a/src/streamer/timesheet/configuration.py +++ b/src/streamd/timesheet/configuration.py @@ -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, diff --git a/src/streamer/timesheet/extract.py b/src/streamd/timesheet/extract.py similarity index 97% rename from src/streamer/timesheet/extract.py rename to src/streamd/timesheet/extract.py index 85547cd..0754f7d 100644 --- a/src/streamer/timesheet/extract.py +++ b/src/streamd/timesheet/extract.py @@ -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 diff --git a/src/streamer/timesheet/timecard.py b/src/streamd/timesheet/timecard.py similarity index 100% rename from src/streamer/timesheet/timecard.py rename to src/streamd/timesheet/timecard.py diff --git a/src/streamer/parse/markdown_tag.py b/src/streamer/parse/markdown_tag.py deleted file mode 100644 index 4de6d35..0000000 --- a/src/streamer/parse/markdown_tag.py +++ /dev/null @@ -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"] diff --git a/streamd.svg b/streamd.svg new file mode 100644 index 0000000..11973db --- /dev/null +++ b/streamd.svg @@ -0,0 +1,283 @@ + + + + + Streamd + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Streamd + + + Konstantin Fickel + + + + + + diff --git a/test/localize/test_extract_datetime.py b/tests/localize/test_extract_datetime.py similarity index 99% rename from test/localize/test_extract_datetime.py rename to tests/localize/test_extract_datetime.py index 279f194..8758ebf 100644 --- a/test/localize/test_extract_datetime.py +++ b/tests/localize/test_extract_datetime.py @@ -1,6 +1,6 @@ from datetime import date, datetime, time -from streamer.localize.extract_datetime import ( +from streamd.localize.extract_datetime import ( extract_date_from_marker, extract_datetime_from_file_name, extract_datetime_from_marker, diff --git a/test/localize/test_repository_configuration_merge.py b/tests/localize/test_repository_configuration_merge.py similarity index 96% rename from test/localize/test_repository_configuration_merge.py rename to tests/localize/test_repository_configuration_merge.py index f5d345d..ba74c76 100644 --- a/test/localize/test_repository_configuration_merge.py +++ b/tests/localize/test_repository_configuration_merge.py @@ -1,6 +1,6 @@ import pytest -from streamer.localize.repository_configuration import ( +from streamd.localize.repository_configuration import ( Dimension, Marker, MarkerPlacement, @@ -252,8 +252,8 @@ class TestMergeRepositoryConfiguration: ), }, markers={ - "Streamer": Marker( - display_name="Streamer", + "Streamd": Marker( + display_name="Streamd", placements=[MarkerPlacement(dimension="project")], ) }, @@ -267,8 +267,8 @@ class TestMergeRepositoryConfiguration: ), }, markers={ - "Streamer": Marker( - display_name="Streamer2", + "Streamd": Marker( + display_name="Streamd2", placements=[ MarkerPlacement( if_with={"Timesheet"}, dimension="timesheet", value="coding" @@ -291,9 +291,9 @@ class TestMergeRepositoryConfiguration: assert merged.dimensions["moment"].display_name == "Moment" assert merged.dimensions["timesheet"].display_name == "Timesheet" - assert set(merged.markers.keys()) == {"Streamer", "JobHunting"} - assert merged.markers["Streamer"].display_name == "Streamer2" - assert merged.markers["Streamer"].placements == [ + assert set(merged.markers.keys()) == {"Streamd", "JobHunting"} + assert merged.markers["Streamd"].display_name == "Streamd2" + assert merged.markers["Streamd"].placements == [ MarkerPlacement(dimension="project", value=None, if_with=set()), MarkerPlacement( if_with={"Timesheet"}, dimension="timesheet", value="coding" @@ -359,7 +359,9 @@ class TestMergeRepositoryConfiguration: ], ) def test_merge_repository_configuration_propagate_preserves_base_when_omitted( - base, second, expected_propagate + base: RepositoryConfiguration, + second: RepositoryConfiguration, + expected_propagate: bool, ): merged = merge_repository_configuration(base, second) assert merged.dimensions["d"].propagate is expected_propagate diff --git a/test/parse/test_parse.py b/tests/parse/test_parse.py similarity index 99% rename from test/parse/test_parse.py rename to tests/parse/test_parse.py index b692120..ef72e5a 100644 --- a/test/parse/test_parse.py +++ b/tests/parse/test_parse.py @@ -1,6 +1,5 @@ from faker import Faker - -from streamer.parse import Shard, StreamFile, parse_markdown_file +from streamd.parse import Shard, StreamFile, parse_markdown_file fake = Faker() diff --git a/test/query/test_find.py b/tests/query/test_find.py similarity index 96% rename from test/query/test_find.py rename to tests/query/test_find.py index 725d3b2..6b72e87 100644 --- a/test/query/test_find.py +++ b/tests/query/test_find.py @@ -2,8 +2,8 @@ from __future__ import annotations from datetime import datetime -from streamer.localize import LocalizedShard -from streamer.query.find import find_shard, find_shard_by_position +from streamd.localize import LocalizedShard +from streamd.query.find import find_shard, find_shard_by_position def generate_localized_shard( diff --git a/test/test_localize.py b/tests/test_localize.py similarity index 93% rename from test/test_localize.py rename to tests/test_localize.py index 678604b..ecdb864 100644 --- a/test/test_localize.py +++ b/tests/test_localize.py @@ -1,14 +1,14 @@ from datetime import datetime -from streamer.localize.localize import localize_stream_file -from streamer.localize.localized_shard import LocalizedShard -from streamer.localize.repository_configuration import ( +from streamd.localize.localize import localize_stream_file +from streamd.localize.localized_shard import LocalizedShard +from streamd.localize.repository_configuration import ( Dimension, Marker, MarkerPlacement, RepositoryConfiguration, ) -from streamer.parse.shard import Shard, StreamFile +from streamd.parse.shard import Shard, StreamFile repository_configuration = RepositoryConfiguration( dimensions={ @@ -29,8 +29,8 @@ repository_configuration = RepositoryConfiguration( ), }, markers={ - "Streamer": Marker( - display_name="Streamer", + "Streamd": Marker( + display_name="Streamd", placements=[ MarkerPlacement(dimension="project"), MarkerPlacement( @@ -49,39 +49,39 @@ class TestLocalize: def test_project_simple_stream_file(self): stream_file = StreamFile( file_name="20250622-121000 Test File.md", - shard=Shard(start_line=1, end_line=1, markers=["Streamer"]), + shard=Shard(start_line=1, end_line=1, markers=["Streamd"]), ) assert localize_stream_file( stream_file, repository_configuration ) == LocalizedShard( moment=datetime(2025, 6, 22, 12, 10, 0, 0), - markers=["Streamer"], + markers=["Streamd"], tags=[], start_line=1, end_line=1, children=[], - location={"project": "Streamer", "file": stream_file.file_name}, + location={"project": "Streamd", "file": stream_file.file_name}, ) def test_timesheet_use_case(self): stream_file = StreamFile( file_name="20260131-210000 Test File.md", - shard=Shard(start_line=1, end_line=1, markers=["Timesheet", "Streamer"]), + shard=Shard(start_line=1, end_line=1, markers=["Timesheet", "Streamd"]), ) assert localize_stream_file( stream_file, repository_configuration ) == LocalizedShard( moment=datetime(2026, 1, 31, 21, 0, 0, 0), - markers=["Timesheet", "Streamer"], + markers=["Timesheet", "Streamd"], tags=[], start_line=1, end_line=1, children=[], location={ "file": stream_file.file_name, - "project": "Streamer", + "project": "Streamd", "timesheet": "coding", }, ) diff --git a/test/timesheet/test_extract_timesheets.py b/tests/timesheet/test_extract_timesheets.py similarity index 96% rename from test/timesheet/test_extract_timesheets.py rename to tests/timesheet/test_extract_timesheets.py index c5a0bbe..b54befc 100644 --- a/test/timesheet/test_extract_timesheets.py +++ b/tests/timesheet/test_extract_timesheets.py @@ -4,13 +4,13 @@ from datetime import datetime, time import pytest -from streamer.localize.localized_shard import LocalizedShard -from streamer.timesheet.configuration import ( +from streamd.localize.localized_shard import LocalizedShard +from streamd.timesheet.configuration import ( TIMESHEET_DIMENSION_NAME, TimesheetPointType, ) -from streamer.timesheet.extract import extract_timesheets -from streamer.timesheet.timecard import SpecialDayType, Timecard, Timesheet +from streamd.timesheet.extract import extract_timesheets +from streamd.timesheet.timecard import SpecialDayType, Timecard, Timesheet def point(at: datetime, type: TimesheetPointType) -> LocalizedShard: @@ -243,7 +243,7 @@ class TestExtractTimesheets: ] with pytest.raises(ValueError, match=r"Last Timecard of .* is not a break"): - extract_timesheets(shards) + _ = extract_timesheets(shards) def test_two_special_day_types_same_day_is_invalid(self): """ @@ -257,7 +257,7 @@ class TestExtractTimesheets: ] with pytest.raises(ValueError, match=r"is both .* and .*"): - extract_timesheets(shards) + _ = extract_timesheets(shards) def test_points_with_mixed_dates_inside_one_group_raises(self): """ @@ -273,7 +273,7 @@ class TestExtractTimesheets: ] with pytest.raises(ValueError, match=r"Last Timecard of .* is not a break"): - extract_timesheets(shards) + _ = extract_timesheets(shards) def test_day_with_only_breaks_is_ignored(self): """ diff --git a/uv.lock b/uv.lock index 4be435e..a843661 100644 --- a/uv.lock +++ b/uv.lock @@ -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.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nodejs-wheel-binaries" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e4/a3/20aa7c4e83f2f614e0036300f3c352775dede0655c66814da16c37b661a9/basedpyright-1.38.2.tar.gz", hash = "sha256:b433b2b8ba745ed7520cdc79a29a03682f3fb00346d272ece5944e9e5e5daa92", size = 25277019, upload-time = "2026-02-26T11:18:43.594Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ac/12/736cab83626fea3fe65cdafb3ef3d2ee9480c56723f2fd33921537289a5e/basedpyright-1.38.2-py3-none-any.whl", hash = "sha256:153481d37fd19f9e3adedc8629d1d071b10c5f5e49321fb026b74444b7c70e24", size = 12312475, upload-time = "2026-02-26T11:18:40.373Z" }, +] + [[package]] name = "click" version = "8.3.1" @@ -43,14 +55,14 @@ wheels = [ [[package]] name = "faker" -version = "40.4.0" +version = "40.11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "tzdata", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fc/7e/dccb7013c9f3d66f2e379383600629fec75e4da2698548bdbf2041ea4b51/faker-40.4.0.tar.gz", hash = "sha256:76f8e74a3df28c3e2ec2caafa956e19e37a132fdc7ea067bc41783affcfee364", size = 1952221, upload-time = "2026-02-06T23:30:15.515Z" } +sdist = { url = "https://files.pythonhosted.org/packages/94/dc/b68e5378e5a7db0ab776efcdd53b6fe374b29d703e156fd5bb4c5437069e/faker-40.11.0.tar.gz", hash = "sha256:7c419299103b13126bd02ec14bd2b47b946edb5a5eedf305e66a193b25f9a734", size = 1957570, upload-time = "2026-03-13T14:36:11.844Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ac/63/58efa67c10fb27810d34351b7a10f85f109a7f7e2a07dc3773952459c47b/faker-40.4.0-py3-none-any.whl", hash = "sha256:486d43c67ebbb136bc932406418744f9a0bdf2c07f77703ea78b58b77e9aa443", size = 1987060, upload-time = "2026-02-06T23:30:13.44Z" }, + { url = "https://files.pythonhosted.org/packages/b1/fa/a86c6ba66f0308c95b9288b1e3eaccd934b545646f63494a86f1ec2f8c8e/faker-40.11.0-py3-none-any.whl", hash = "sha256:0e9816c950528d2a37d74863f3ef389ea9a3a936cbcde0b11b8499942e25bf90", size = 1989457, upload-time = "2026-03-13T14:36:09.792Z" }, ] [[package]] @@ -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,24 +204,20 @@ 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]] name = "pydantic-settings" -version = "2.12.0" +version = "2.13.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pydantic" }, { name = "python-dotenv" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/43/4b/ac7e0aae12027748076d72a8764ff1c9d82ca75a7a52622e67ed3f765c54/pydantic_settings-2.12.0.tar.gz", hash = "sha256:005538ef951e3c2a68e1c08b292b5f2e71490def8589d4221b95dab00dafcfd0", size = 194184, upload-time = "2025-11-10T14:25:47.013Z" } +sdist = { url = "https://files.pythonhosted.org/packages/52/6d/fffca34caecc4a3f97bda81b2098da5e8ab7efc9a66e819074a11955d87e/pydantic_settings-2.13.1.tar.gz", hash = "sha256:b4c11847b15237fb0171e1462bf540e294affb9b86db4d9aa5c01730bdbe4025", size = 223826, upload-time = "2026-02-19T13:45:08.055Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c1/60/5d4751ba3f4a40a6891f24eec885f51afd78d208498268c734e256fb13c4/pydantic_settings-2.12.0-py3-none-any.whl", hash = "sha256:fddb9fd99a5b18da837b29710391e945b1e30c135477f484084ee513adb93809", size = 51880, upload-time = "2025-11-10T14:25:45.546Z" }, + { url = "https://files.pythonhosted.org/packages/00/4b/ccc026168948fec4f7555b9164c724cf4125eac006e176541483d2c959be/pydantic_settings-2.13.1-py3-none-any.whl", hash = "sha256:d56fd801823dbeae7f0975e1f8c8e25c258eb75d278ea7abb5d9cebb01b56237", size = 58929, upload-time = "2026-02-19T13:45:06.034Z" }, ] [package.optional-dependencies] @@ -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" }, @@ -319,40 +297,40 @@ wheels = [ [[package]] name = "rich" -version = "14.3.2" +version = "14.3.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markdown-it-py" }, { name = "pygments" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/74/99/a4cab2acbb884f80e558b0771e97e21e939c5dfb460f488d19df485e8298/rich-14.3.2.tar.gz", hash = "sha256:e712f11c1a562a11843306f5ed999475f09ac31ffb64281f73ab29ffdda8b3b8", size = 230143, upload-time = "2026-02-01T16:20:47.908Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b3/c6/f3b320c27991c46f43ee9d856302c70dc2d0fb2dba4842ff739d5f46b393/rich-14.3.3.tar.gz", hash = "sha256:b8daa0b9e4eef54dd8cf7c86c03713f53241884e814f4e2f5fb342fe520f639b", size = 230582, upload-time = "2026-02-19T17:23:12.474Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/45/615f5babd880b4bd7d405cc0dc348234c5ffb6ed1ea33e152ede08b2072d/rich-14.3.2-py3-none-any.whl", hash = "sha256:08e67c3e90884651da3239ea668222d19bea7b589149d8014a21c633420dbb69", size = 309963, upload-time = "2026-02-01T16:20:46.078Z" }, + { url = "https://files.pythonhosted.org/packages/14/25/b208c5683343959b670dc001595f2f3737e051da617f66c31f7c4fa93abc/rich-14.3.3-py3-none-any.whl", hash = "sha256:793431c1f8619afa7d3b52b2cdec859562b950ea0d4b6b505397612db8d5362d", size = 310458, upload-time = "2026-02-19T17:23:13.732Z" }, ] [[package]] name = "ruff" -version = "0.15.0" +version = "0.15.6" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c8/39/5cee96809fbca590abea6b46c6d1c586b49663d1d2830a751cc8fc42c666/ruff-0.15.0.tar.gz", hash = "sha256:6bdea47cdbea30d40f8f8d7d69c0854ba7c15420ec75a26f463290949d7f7e9a", size = 4524893, upload-time = "2026-02-03T17:53:35.357Z" } +sdist = { url = "https://files.pythonhosted.org/packages/51/df/f8629c19c5318601d3121e230f74cbee7a3732339c52b21daa2b82ef9c7d/ruff-0.15.6.tar.gz", hash = "sha256:8394c7bb153a4e3811a4ecdacd4a8e6a4fa8097028119160dffecdcdf9b56ae4", size = 4597916, upload-time = "2026-03-12T23:05:47.51Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/bc/88/3fd1b0aa4b6330d6aaa63a285bc96c9f71970351579152d231ed90914586/ruff-0.15.0-py3-none-linux_armv6l.whl", hash = "sha256:aac4ebaa612a82b23d45964586f24ae9bc23ca101919f5590bdb368d74ad5455", size = 10354332, upload-time = "2026-02-03T17:52:54.892Z" }, - { url = "https://files.pythonhosted.org/packages/72/f6/62e173fbb7eb75cc29fe2576a1e20f0a46f671a2587b5f604bfb0eaf5f6f/ruff-0.15.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:dcd4be7cc75cfbbca24a98d04d0b9b36a270d0833241f776b788d59f4142b14d", size = 10767189, upload-time = "2026-02-03T17:53:19.778Z" }, - { url = "https://files.pythonhosted.org/packages/99/e4/968ae17b676d1d2ff101d56dc69cf333e3a4c985e1ec23803df84fc7bf9e/ruff-0.15.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d747e3319b2bce179c7c1eaad3d884dc0a199b5f4d5187620530adf9105268ce", size = 10075384, upload-time = "2026-02-03T17:53:29.241Z" }, - { url = "https://files.pythonhosted.org/packages/a2/bf/9843c6044ab9e20af879c751487e61333ca79a2c8c3058b15722386b8cae/ruff-0.15.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:650bd9c56ae03102c51a5e4b554d74d825ff3abe4db22b90fd32d816c2e90621", size = 10481363, upload-time = "2026-02-03T17:52:43.332Z" }, - { url = "https://files.pythonhosted.org/packages/55/d9/4ada5ccf4cd1f532db1c8d44b6f664f2208d3d93acbeec18f82315e15193/ruff-0.15.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a6664b7eac559e3048223a2da77769c2f92b43a6dfd4720cef42654299a599c9", size = 10187736, upload-time = "2026-02-03T17:53:00.522Z" }, - { url = "https://files.pythonhosted.org/packages/86/e2/f25eaecd446af7bb132af0a1d5b135a62971a41f5366ff41d06d25e77a91/ruff-0.15.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f811f97b0f092b35320d1556f3353bf238763420ade5d9e62ebd2b73f2ff179", size = 10968415, upload-time = "2026-02-03T17:53:15.705Z" }, - { url = "https://files.pythonhosted.org/packages/e7/dc/f06a8558d06333bf79b497d29a50c3a673d9251214e0d7ec78f90b30aa79/ruff-0.15.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:761ec0a66680fab6454236635a39abaf14198818c8cdf691e036f4bc0f406b2d", size = 11809643, upload-time = "2026-02-03T17:53:23.031Z" }, - { url = "https://files.pythonhosted.org/packages/dd/45/0ece8db2c474ad7df13af3a6d50f76e22a09d078af63078f005057ca59eb/ruff-0.15.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:940f11c2604d317e797b289f4f9f3fa5555ffe4fb574b55ed006c3d9b6f0eb78", size = 11234787, upload-time = "2026-02-03T17:52:46.432Z" }, - { url = "https://files.pythonhosted.org/packages/8a/d9/0e3a81467a120fd265658d127db648e4d3acfe3e4f6f5d4ea79fac47e587/ruff-0.15.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bcbca3d40558789126da91d7ef9a7c87772ee107033db7191edefa34e2c7f1b4", size = 11112797, upload-time = "2026-02-03T17:52:49.274Z" }, - { url = "https://files.pythonhosted.org/packages/b2/cb/8c0b3b0c692683f8ff31351dfb6241047fa873a4481a76df4335a8bff716/ruff-0.15.0-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:9a121a96db1d75fa3eb39c4539e607f628920dd72ff1f7c5ee4f1b768ac62d6e", size = 11033133, upload-time = "2026-02-03T17:53:33.105Z" }, - { url = "https://files.pythonhosted.org/packages/f8/5e/23b87370cf0f9081a8c89a753e69a4e8778805b8802ccfe175cc410e50b9/ruff-0.15.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:5298d518e493061f2eabd4abd067c7e4fb89e2f63291c94332e35631c07c3662", size = 10442646, upload-time = "2026-02-03T17:53:06.278Z" }, - { url = "https://files.pythonhosted.org/packages/e1/9a/3c94de5ce642830167e6d00b5c75aacd73e6347b4c7fc6828699b150a5ee/ruff-0.15.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:afb6e603d6375ff0d6b0cee563fa21ab570fd15e65c852cb24922cef25050cf1", size = 10195750, upload-time = "2026-02-03T17:53:26.084Z" }, - { url = "https://files.pythonhosted.org/packages/30/15/e396325080d600b436acc970848d69df9c13977942fb62bb8722d729bee8/ruff-0.15.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:77e515f6b15f828b94dc17d2b4ace334c9ddb7d9468c54b2f9ed2b9c1593ef16", size = 10676120, upload-time = "2026-02-03T17:53:09.363Z" }, - { url = "https://files.pythonhosted.org/packages/8d/c9/229a23d52a2983de1ad0fb0ee37d36e0257e6f28bfd6b498ee2c76361874/ruff-0.15.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:6f6e80850a01eb13b3e42ee0ebdf6e4497151b48c35051aab51c101266d187a3", size = 11201636, upload-time = "2026-02-03T17:52:57.281Z" }, - { url = "https://files.pythonhosted.org/packages/6f/b0/69adf22f4e24f3677208adb715c578266842e6e6a3cc77483f48dd999ede/ruff-0.15.0-py3-none-win32.whl", hash = "sha256:238a717ef803e501b6d51e0bdd0d2c6e8513fe9eec14002445134d3907cd46c3", size = 10465945, upload-time = "2026-02-03T17:53:12.591Z" }, - { url = "https://files.pythonhosted.org/packages/51/ad/f813b6e2c97e9b4598be25e94a9147b9af7e60523b0cb5d94d307c15229d/ruff-0.15.0-py3-none-win_amd64.whl", hash = "sha256:dd5e4d3301dc01de614da3cdffc33d4b1b96fb89e45721f1598e5532ccf78b18", size = 11564657, upload-time = "2026-02-03T17:52:51.893Z" }, - { url = "https://files.pythonhosted.org/packages/f6/b0/2d823f6e77ebe560f4e397d078487e8d52c1516b331e3521bc75db4272ca/ruff-0.15.0-py3-none-win_arm64.whl", hash = "sha256:c480d632cc0ca3f0727acac8b7d053542d9e114a462a145d0b00e7cd658c515a", size = 10865753, upload-time = "2026-02-03T17:53:03.014Z" }, + { url = "https://files.pythonhosted.org/packages/9e/2f/4e03a7e5ce99b517e98d3b4951f411de2b0fa8348d39cf446671adcce9a2/ruff-0.15.6-py3-none-linux_armv6l.whl", hash = "sha256:7c98c3b16407b2cf3d0f2b80c80187384bc92c6774d85fefa913ecd941256fff", size = 10508953, upload-time = "2026-03-12T23:05:17.246Z" }, + { url = "https://files.pythonhosted.org/packages/70/60/55bcdc3e9f80bcf39edf0cd272da6fa511a3d94d5a0dd9e0adf76ceebdb4/ruff-0.15.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ee7dcfaad8b282a284df4aa6ddc2741b3f4a18b0555d626805555a820ea181c3", size = 10942257, upload-time = "2026-03-12T23:05:23.076Z" }, + { url = "https://files.pythonhosted.org/packages/e7/f9/005c29bd1726c0f492bfa215e95154cf480574140cb5f867c797c18c790b/ruff-0.15.6-py3-none-macosx_11_0_arm64.whl", hash = "sha256:3bd9967851a25f038fc8b9ae88a7fbd1b609f30349231dffaa37b6804923c4bb", size = 10322683, upload-time = "2026-03-12T23:05:33.738Z" }, + { url = "https://files.pythonhosted.org/packages/5f/74/2f861f5fd7cbb2146bddb5501450300ce41562da36d21868c69b7a828169/ruff-0.15.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:13f4594b04e42cd24a41da653886b04d2ff87adbf57497ed4f728b0e8a4866f8", size = 10660986, upload-time = "2026-03-12T23:05:53.245Z" }, + { url = "https://files.pythonhosted.org/packages/c1/a1/309f2364a424eccb763cdafc49df843c282609f47fe53aa83f38272389e0/ruff-0.15.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e2ed8aea2f3fe57886d3f00ea5b8aae5bf68d5e195f487f037a955ff9fbaac9e", size = 10332177, upload-time = "2026-03-12T23:05:56.145Z" }, + { url = "https://files.pythonhosted.org/packages/30/41/7ebf1d32658b4bab20f8ac80972fb19cd4e2c6b78552be263a680edc55ac/ruff-0.15.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:70789d3e7830b848b548aae96766431c0dc01a6c78c13381f423bf7076c66d15", size = 11170783, upload-time = "2026-03-12T23:06:01.742Z" }, + { url = "https://files.pythonhosted.org/packages/76/be/6d488f6adca047df82cd62c304638bcb00821c36bd4881cfca221561fdfc/ruff-0.15.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:542aaf1de3154cea088ced5a819ce872611256ffe2498e750bbae5247a8114e9", size = 12044201, upload-time = "2026-03-12T23:05:28.697Z" }, + { url = "https://files.pythonhosted.org/packages/71/68/e6f125df4af7e6d0b498f8d373274794bc5156b324e8ab4bf5c1b4fc0ec7/ruff-0.15.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c22e6f02c16cfac3888aa636e9eba857254d15bbacc9906c9689fdecb1953ab", size = 11421561, upload-time = "2026-03-12T23:05:31.236Z" }, + { url = "https://files.pythonhosted.org/packages/f1/9f/f85ef5fd01a52e0b472b26dc1b4bd228b8f6f0435975442ffa4741278703/ruff-0.15.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98893c4c0aadc8e448cfa315bd0cc343a5323d740fe5f28ef8a3f9e21b381f7e", size = 11310928, upload-time = "2026-03-12T23:05:45.288Z" }, + { url = "https://files.pythonhosted.org/packages/8c/26/b75f8c421f5654304b89471ed384ae8c7f42b4dff58fa6ce1626d7f2b59a/ruff-0.15.6-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:70d263770d234912374493e8cc1e7385c5d49376e41dfa51c5c3453169dc581c", size = 11235186, upload-time = "2026-03-12T23:05:50.677Z" }, + { url = "https://files.pythonhosted.org/packages/fc/d4/d5a6d065962ff7a68a86c9b4f5500f7d101a0792078de636526c0edd40da/ruff-0.15.6-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:55a1ad63c5a6e54b1f21b7514dfadc0c7fb40093fa22e95143cf3f64ebdcd512", size = 10635231, upload-time = "2026-03-12T23:05:37.044Z" }, + { url = "https://files.pythonhosted.org/packages/d6/56/7c3acf3d50910375349016cf33de24be021532042afbed87942858992491/ruff-0.15.6-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8dc473ba093c5ec238bb1e7429ee676dca24643c471e11fbaa8a857925b061c0", size = 10340357, upload-time = "2026-03-12T23:06:04.748Z" }, + { url = "https://files.pythonhosted.org/packages/06/54/6faa39e9c1033ff6a3b6e76b5df536931cd30caf64988e112bbf91ef5ce5/ruff-0.15.6-py3-none-musllinux_1_2_i686.whl", hash = "sha256:85b042377c2a5561131767974617006f99f7e13c63c111b998f29fc1e58a4cfb", size = 10860583, upload-time = "2026-03-12T23:05:58.978Z" }, + { url = "https://files.pythonhosted.org/packages/cb/1e/509a201b843b4dfb0b32acdedf68d951d3377988cae43949ba4c4133a96a/ruff-0.15.6-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:cef49e30bc5a86a6a92098a7fbf6e467a234d90b63305d6f3ec01225a9d092e0", size = 11410976, upload-time = "2026-03-12T23:05:39.955Z" }, + { url = "https://files.pythonhosted.org/packages/6c/25/3fc9114abf979a41673ce877c08016f8e660ad6cf508c3957f537d2e9fa9/ruff-0.15.6-py3-none-win32.whl", hash = "sha256:bbf67d39832404812a2d23020dda68fee7f18ce15654e96fb1d3ad21a5fe436c", size = 10616872, upload-time = "2026-03-12T23:05:42.451Z" }, + { url = "https://files.pythonhosted.org/packages/89/7a/09ece68445ceac348df06e08bf75db72d0e8427765b96c9c0ffabc1be1d9/ruff-0.15.6-py3-none-win_amd64.whl", hash = "sha256:aee25bc84c2f1007ecb5037dff75cef00414fdf17c23f07dc13e577883dca406", size = 11787271, upload-time = "2026-03-12T23:05:20.168Z" }, + { url = "https://files.pythonhosted.org/packages/7f/d0/578c47dd68152ddddddf31cd7fc67dc30b7cdf639a86275fda821b0d9d98/ruff-0.15.6-py3-none-win_arm64.whl", hash = "sha256:c34de3dd0b0ba203be50ae70f5910b17188556630e2178fd7d79fc030eb0d837", size = 11060497, upload-time = "2026-03-12T23:05:25.968Z" }, ] [[package]] @@ -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" }, ] @@ -391,23 +369,23 @@ requires-dist = [ { name = "click", specifier = "==8.3.1" }, { name = "mistletoe", specifier = "==1.5.1" }, { name = "pydantic", specifier = "==2.12.5" }, - { name = "pydantic-settings", extras = ["yaml"], specifier = "==2.12.0" }, - { name = "rich", specifier = "==14.3.2" }, - { name = "typer", specifier = "==0.21.2" }, + { name = "pydantic-settings", extras = ["yaml"], specifier = "==2.13.1" }, + { name = "rich", specifier = "==14.3.3" }, + { name = "typer", specifier = "==0.24.1" }, { name = "xdg-base-dirs", specifier = "==6.0.2" }, ] [package.metadata.requires-dev] dev = [ - { name = "faker", specifier = "==40.4.0" }, - { name = "pyright", specifier = "==1.1.408" }, + { name = "basedpyright", specifier = "==1.38.2" }, + { name = "faker", specifier = "==40.11.0" }, { name = "pytest", specifier = "==9.0.2" }, - { name = "ruff", specifier = "==0.15.0" }, + { name = "ruff", specifier = "==0.15.6" }, ] [[package]] name = "typer" -version = "0.21.2" +version = "0.24.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-doc" }, @@ -415,9 +393,9 @@ dependencies = [ { name = "rich" }, { name = "shellingham" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f2/1e/a27cc02a0cd715118c71fa2aef2c687fdefc3c28d90fd0dd789c5118154c/typer-0.21.2.tar.gz", hash = "sha256:1abd95a3b675e17ff61b0838ac637fe9478d446d62ad17fa4bb81ea57cc54028", size = 120426, upload-time = "2026-02-10T19:33:46.182Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f5/24/cb09efec5cc954f7f9b930bf8279447d24618bb6758d4f6adf2574c41780/typer-0.24.1.tar.gz", hash = "sha256:e39b4732d65fbdcde189ae76cf7cd48aeae72919dea1fdfc16593be016256b45", size = 118613, upload-time = "2026-02-21T16:54:40.609Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b8/cc/d59f893fbdfb5f58770c05febfc4086a46875f1084453621c35605cec946/typer-0.21.2-py3-none-any.whl", hash = "sha256:c3d8de54d00347ef90b82131ca946274f017cffb46683ae3883c360fa958f55c", size = 56728, upload-time = "2026-02-10T19:33:48.01Z" }, + { url = "https://files.pythonhosted.org/packages/4a/91/48db081e7a63bb37284f9fbcefda7c44c277b18b0e13fbc36ea2335b71e6/typer-0.24.1-py3-none-any.whl", hash = "sha256:112c1f0ce578bfb4cab9ffdabc68f031416ebcc216536611ba21f04e9aa84c9e", size = 56085, upload-time = "2026-02-21T16:54:41.616Z" }, ] [[package]]