Skip to content

Conversation

@klingonaston
Copy link
Collaborator

@klingonaston klingonaston commented Dec 3, 2025

Changes requested:

Create time_tracker.py in app/core/. Add functions start_timer(report_id) and finish_timer(report_id) that record start and end timestamps using database storage or Redis. Compute total time taken for each report.

Please review.

Summary by CodeRabbit

  • Chores
    • Updated repository configuration to exclude virtual environment and Python cache artifacts from version control.
    • Cleaned up local development environment files.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Dec 3, 2025

Walkthrough

Added a .gitignore file to exclude virtual environment directories and Python cache artifacts. Removed virtual environment bin scripts (python, python3, python3.12) from the repository, consolidating project housekeeping by preventing future tracking of environment-specific files.

Changes

Cohort / File(s) Summary
Git configuration
.gitignore
Added new .gitignore file with patterns to ignore virtual environment folders (.venv/, venv/, ENV/, env/) and Python cache files (pycache/, *.pyc)
Virtual environment cleanup
.venv/bin/python, .venv/bin/python3, .venv/bin/python3.12
Deleted three Python launcher stubs and wrapper scripts from the .venv/bin directory

Estimated code review effort

🎯 1 (Trivial) | ⏱️ ~2 minutes

  • Simple configuration and cleanup with no functional logic changes
  • .gitignore patterns are standard and straightforward
  • Virtual environment files are non-essential to codebase

Poem

🐰 A tidy warren is a happy warren! ✨
Virtual venvs hidden from sight,
.gitignore keeps the repo clean and light,
Now only the essentials remain so bright!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 42.77% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check ❓ Inconclusive The title is incomplete and truncated, ending with 'Add funct', which prevents clear understanding of the main changes despite referencing the core file creation. Complete the title to clearly describe what functions are being added, e.g., 'Create time_tracker.py with start_timer and finish_timer functions' or similar concise description.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/20251203143530

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
backend/app/core/time_tracker.py (2)

9-19: Consider using lazy formatting in logger calls.

The function logic is correct and error handling is appropriate. However, f-strings in logger calls are evaluated eagerly even when the log level would skip them, which can impact performance in high-throughput scenarios.

Apply this diff to use lazy % formatting:

-        logger.info(f"Timer started for report_id: {report_id} at {start_time}")
+        logger.info("Timer started for report_id: %s at %s", report_id, start_time)
-        logger.error(f"Failed to start timer for report_id {report_id}: {e}", exc_info=True)
+        logger.error("Failed to start timer for report_id %s: %s", report_id, e, exc_info=True)

34-34: Consider using lazy formatting in logger calls.

Similar to start_timer, these logger calls use f-strings that are evaluated eagerly. For consistency and performance, use lazy % formatting.

Apply this diff:

-            logger.info(f"Timer finished for report_id: {report_id}. Duration: {duration:.2f} seconds.")
+            logger.info("Timer finished for report_id: %s. Duration: %.2f seconds.", report_id, duration)
-            logger.warning(f"Timer not found for report_id: {report_id}")
+            logger.warning("Timer not found for report_id: %s", report_id)
-        logger.error(f"Failed to finish timer for report_id {report_id}: {e}", exc_info=True)
+        logger.error("Failed to finish timer for report_id %s: %s", report_id, e, exc_info=True)

Also applies to: 37-37, 40-40

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ea04f51 and 45205b9.

📒 Files selected for processing (1)
  • backend/app/core/time_tracker.py (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
backend/app/core/time_tracker.py (1)
backend/app/cache/redis_client.py (3)
  • set_cache (37-49)
  • get_cache (51-63)
  • delete_cache (65-75)
🔇 Additional comments (2)
backend/app/core/time_tracker.py (2)

1-7: LGTM!

The imports and module setup are correct. The Redis key prefix provides clear namespace isolation for timer keys.


21-21: Document Python version requirement or use from __future__ import annotations for compatibility.

The float | None syntax (PEP 604) requires Python 3.10 or higher. This pattern is used throughout the codebase, suggesting intentional targeting of Python 3.10+. However, there is no explicit Python version requirement in pyproject.toml or setup.py. Either add python_requires = ">=3.10" to your project configuration, or use from __future__ import annotations (available in Python 3.7+) to enable this syntax on earlier versions.

Comment on lines 28 to 31
start_time_str = redis_client.get_cache(key)
if start_time_str:
redis_client.delete_cache(key)
start_time = datetime.fromisoformat(start_time_str)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: Redis returns bytes, not strings.

Line 31 will raise a TypeError because redis_client.get_cache() returns bytes from the Redis client, but datetime.fromisoformat() expects a string. You must decode the bytes before parsing.

Apply this diff to fix the issue:

         start_time_str = redis_client.get_cache(key)
         if start_time_str:
             redis_client.delete_cache(key)
-            start_time = datetime.fromisoformat(start_time_str)
+            start_time = datetime.fromisoformat(start_time_str.decode('utf-8'))
             end_time = datetime.now()
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
start_time_str = redis_client.get_cache(key)
if start_time_str:
redis_client.delete_cache(key)
start_time = datetime.fromisoformat(start_time_str)
start_time_str = redis_client.get_cache(key)
if start_time_str:
redis_client.delete_cache(key)
start_time = datetime.fromisoformat(start_time_str.decode('utf-8'))
🤖 Prompt for AI Agents
In backend/app/core/time_tracker.py around lines 28 to 31,
redis_client.get_cache() returns bytes but the code passes that directly to
datetime.fromisoformat(), causing a TypeError; decode the returned value to a
string (e.g., call .decode('utf-8') or use .decode() if not None) before calling
datetime.fromisoformat(), and optionally guard with an isinstance check or a
conditional to only decode when the value is bytes.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 26

Note

Due to the large number of review comments, Critical severity comments were prioritized as inline comments.

♻️ Duplicate comments (22)
.venv/lib/python3.12/site-packages/SQLAlchemy-2.0.31.dist-info/METADATA (1)

1-242: Same issue: virtual environment file should not be committed.

This file is part of the same .venv/ directory that should be excluded from version control. See the previous comment on the RECORD file.

.venv/lib/python3.12/site-packages/_pytest/_code/__init__.py (1)

1-24: Virtual environment file - should not be committed.

Same issue as noted above. This pytest library file should be removed along with the entire .venv/ directory.

.venv/lib/python3.12/site-packages/_pytest/_code/code.py (1)

1-1411: Virtual environment file - should not be committed.

Same issue as noted above. This pytest library file should be removed along with the entire .venv/ directory.

.venv/bin/fastapi (1)

1-8: Virtualenv shim should not be tracked (duplicate of pip3.12 comment)

This FastAPI CLI wrapper is a generated .venv script with a local-path shebang and no project-specific logic. It should be removed from git along with the rest of .venv (see comment on .venv/bin/pip3.12).

.venv/bin/uvicorn (1)

1-8: Generated uvicorn launcher should not live in source control

This is another venv-generated entrypoint with an absolute shebang. It should be excluded from the repo together with the rest of .venv (see .venv/bin/pip3.12 review).

.venv/bin/alembic (1)

1-8: Alembic CLI shim is environment-specific and shouldn’t be committed

This wrapper is auto-generated by the virtualenv and depends on a hard-coded path. Remove it (and .venv generally) from version control; rely on pip install + alembic from the venv instead.

.venv/bin/dotenv (1)

1-8: Dotenv CLI wrapper is a generated artifact

As with the other .venv/bin/* scripts, this is a generated launcher and should not be versioned. Please remove it along with the rest of the virtualenv directory.

.venv/bin/nltk (1)

1-8: NLTK launcher is venv-generated and should be excluded

This script is produced by the venv tooling and includes an absolute shebang. It should not be tracked; prefer ignoring .venv/ entirely.

.venv/bin/tqdm (1)

1-8: Tqdm CLI shim is not application code

This is another auto-generated .venv/bin script. Please remove it from git and rely on recreating the venv from pyproject.toml/requirements.txt instead.

.venv/bin/normalizer (1)

1-8: Charset-normalizer CLI shim should be removed with the venv

This is a virtualenv-generated entrypoint and not part of the app’s source. Please delete it (and .venv contents) from version control and ensure .venv/ is ignored going forward.

.venv/bin/activate.fish (1)

1-69: Auto-generated virtual environment file should not be committed.

This Fish shell activation script is auto-generated by Python's venv module and contains a hardcoded path on line 36 specific to the original developer's machine. As noted in the previous file, the entire .venv/ directory should be added to .gitignore and removed from version control.

.venv/bin/Activate.ps1 (1)

1-247: Auto-generated virtual environment file should not be committed.

This PowerShell activation script is auto-generated by Python's venv module. While it dynamically derives paths, the entire .venv/ directory should be excluded from version control as previously noted.

.venv/bin/activate.csh (1)

1-27: Auto-generated virtual environment file should not be committed.

This C shell activation script contains a hardcoded path on line 12. As noted previously, remove the .venv/ directory from version control.

.venv/bin/activate (1)

1-70: Auto-generated virtual environment file should not be committed.

This Bash activation script contains hardcoded paths on lines 42 and 45. As noted previously, the entire .venv/ directory should be removed from version control and added to .gitignore.

Additionally: The PR description states this adds time_tracker.py in app/core/ with timer functions, but none of the files under review match that description. Please verify that the intended time_tracker.py file is included in this PR and that these .venv/ files were not accidentally staged.

.venv/lib/python3.12/site-packages/_pytest/__init__.py (1)

1-10: Virtual environment file - should not be committed.

This is the pytest package's __init__.py from the virtual environment. As noted in the previous file, all .venv/ contents should be removed from this PR and added to .gitignore.

.venv/lib/python3.12/site-packages/_pytest/_io/wcwidth.py (1)

1-55: Virtual environment file - should not be committed.

This pytest terminal width utility module is part of the virtual environment and should not be in version control.

.venv/lib/python3.12/site-packages/_pytest/assertion/truncate.py (1)

1-118: Virtual environment file - should not be committed.

This is pytest's assertion truncation module from the virtual environment. The static analysis warnings (unused max_length parameter on line 21, loop variable on line 106) are in third-party pytest code and not within your control to fix.

Remove all .venv/ files from this PR.

.venv/lib/python3.12/site-packages/_pytest/_io/pprint.py (1)

1-676: Virtual environment file - should not be committed.

This is pytest's vendored pretty-printer (originally from CPython) located in the virtual environment. The static analysis warnings are expected in vendored third-party code and are not actionable.

Summary for this PR: None of the 5 files submitted for review should be committed. They are all pytest library files from .venv/. The actual time_tracker.py file mentioned in the PR description (app/core/time_tracker.py with start_timer and finish_timer functions) is not present in this review.

Please:

  1. Remove .venv/ from git tracking: git rm -r --cached .venv/
  2. Add .venv/ to .gitignore
  3. Ensure the intended app/core/time_tracker.py file is properly staged and committed
.venv/lib/python3.12/site-packages/_pytest/_io/terminalwriter.py (1)

1-245: Virtual environment file - should not be committed.

This is a third-party pytest library file. See the comment on .venv/lib/python3.12/site-packages/_pytest/assertion/__init__.py for remediation steps.

.venv/lib/python3.12/site-packages/_pytest/debugging.py (1)

1-392: Virtual environment file - should not be committed.

This is a third-party pytest library file. See the comment on .venv/lib/python3.12/site-packages/_pytest/assertion/__init__.py for remediation steps.

.venv/lib/python3.12/site-packages/_pytest/assertion/util.py (1)

1-609: Virtual environment file - should not be committed.

This is a third-party pytest library file. See the comment on .venv/lib/python3.12/site-packages/_pytest/assertion/__init__.py for remediation steps.

.venv/lib/python3.12/site-packages/_pytest/assertion/rewrite.py (1)

1-1191: Virtual environment file - should not be committed.

This is a third-party pytest library file. See the comment on .venv/lib/python3.12/site-packages/_pytest/assertion/__init__.py for remediation steps.

🟠 Major comments (3)
.venv/bin/py.test-1-8 (1)

1-8: Do not commit virtualenv-generated pytest launcher scripts

This file is an auto-generated shim inside .venv/ with an absolute shebang pointing to your local path:

  • Line 1 hardcodes /home/repositories/.../.venv/bin/python3, which will not exist on other machines or CI.
  • Everything under .venv/ is environment-specific and should generally be excluded from version control.

Recommend:

  • Remove .venv/bin/py.test (and other .venv contents) from the repo.
  • Add .venv/ to .gitignore (or equivalent) so these artifacts are not tracked.
  • Rely on pytest from the active virtualenv instead of this committed shim.
.venv/lib/python3.12/site-packages/_pytest/deprecated.py-1-89 (1)

1-89: Do not commit .venv / site-packages; this file is third‑party and unrelated to the PR goal

This module is part of pytest inside a virtualenv (.venv/lib/python3.12/site-packages/_pytest/...), not application code. Checking in .venv / site-packages will bloat the repo and tightly couple it to a specific local environment, and any edits here will be overwritten on dependency upgrades.

  • Please remove this file (and the rest of .venv) from version control and add .venv/ to .gitignore (or equivalent ignore mechanism).
  • The PR description mentions adding app/core/time_tracker.py with start_timer / finish_timer, but that file is not present in the diff you shared. Make sure only your actual application code (e.g., app/core/time_tracker.py) and any necessary tests/config are included in the PR, not generated environment artifacts.
.venv/bin/pip3.12-1-8 (1)

1-8: Do not commit virtualenv binaries to the repository

This is an auto-generated pip shim inside .venv with a machine-specific shebang. Keeping these in git will:

  • Break on other machines/paths.
  • Create noisy diffs whenever the venv is recreated or upgraded.
  • Bloat the repo with generated tooling.

Recommend:

  • Remove the entire .venv/ directory from version control.
  • Add it to .gitignore (or equivalent) so only application code (e.g., app/core/time_tracker.py mentioned in the PR description) is tracked.

Example .gitignore snippet:

+.venv/

Committable suggestion skipped: line range outside the PR's diff.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 45205b9 and 962ad1b.

⛔ Files ignored due to path filters (127)
  • .venv/lib/python3.12/site-packages/__pycache__/py.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/__pycache__/typing_extensions.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/_pytest/__pycache__/__init__.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/_pytest/__pycache__/_argcomplete.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/_pytest/__pycache__/_version.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/_pytest/__pycache__/cacheprovider.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/_pytest/__pycache__/capture.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/_pytest/__pycache__/compat.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/_pytest/__pycache__/debugging.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/_pytest/__pycache__/deprecated.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/_pytest/__pycache__/doctest.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/_pytest/__pycache__/faulthandler.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/_pytest/__pycache__/fixtures.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/_pytest/__pycache__/freeze_support.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/_pytest/__pycache__/helpconfig.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/_pytest/__pycache__/hookspec.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/_pytest/__pycache__/junitxml.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/_pytest/__pycache__/legacypath.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/_pytest/__pycache__/logging.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/_pytest/__pycache__/main.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/_pytest/__pycache__/monkeypatch.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/_pytest/__pycache__/nodes.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/_pytest/__pycache__/outcomes.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/_pytest/__pycache__/pastebin.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/_pytest/__pycache__/pathlib.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/_pytest/__pycache__/pytester.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/_pytest/__pycache__/pytester_assertions.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/_pytest/__pycache__/python.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/_pytest/__pycache__/python_api.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/_pytest/__pycache__/python_path.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/_pytest/__pycache__/recwarn.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/_pytest/__pycache__/reports.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/_pytest/__pycache__/runner.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/_pytest/__pycache__/scope.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/_pytest/__pycache__/setuponly.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/_pytest/__pycache__/setupplan.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/_pytest/__pycache__/skipping.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/_pytest/__pycache__/stash.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/_pytest/__pycache__/stepwise.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/_pytest/__pycache__/terminal.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/_pytest/__pycache__/threadexception.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/_pytest/__pycache__/timing.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/_pytest/__pycache__/tmpdir.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/_pytest/__pycache__/unittest.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/_pytest/__pycache__/unraisableexception.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/_pytest/__pycache__/warning_types.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/_pytest/__pycache__/warnings.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/_pytest/_code/__pycache__/__init__.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/_pytest/_code/__pycache__/code.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/_pytest/_code/__pycache__/source.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/_pytest/_io/__pycache__/__init__.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/_pytest/_io/__pycache__/pprint.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/_pytest/_io/__pycache__/saferepr.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/_pytest/_io/__pycache__/terminalwriter.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/_pytest/_io/__pycache__/wcwidth.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/_pytest/_py/__pycache__/__init__.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/_pytest/_py/__pycache__/error.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/_pytest/_py/__pycache__/path.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/_pytest/assertion/__pycache__/__init__.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/_pytest/assertion/__pycache__/rewrite.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/_pytest/assertion/__pycache__/truncate.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/_pytest/assertion/__pycache__/util.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/_pytest/config/__pycache__/__init__.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/_pytest/config/__pycache__/argparsing.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/_pytest/config/__pycache__/compat.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/_pytest/config/__pycache__/exceptions.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/_pytest/config/__pycache__/findpaths.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/_pytest/mark/__pycache__/__init__.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/_pytest/mark/__pycache__/expression.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/_pytest/mark/__pycache__/structures.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/alembic/__pycache__/__init__.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/alembic/__pycache__/__main__.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/alembic/__pycache__/command.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/alembic/__pycache__/config.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/alembic/__pycache__/context.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/alembic/__pycache__/environment.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/alembic/__pycache__/migration.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/alembic/__pycache__/op.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/alembic/autogenerate/__pycache__/__init__.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/alembic/autogenerate/__pycache__/api.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/alembic/autogenerate/__pycache__/compare.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/alembic/autogenerate/__pycache__/render.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/alembic/autogenerate/__pycache__/rewriter.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/alembic/ddl/__pycache__/__init__.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/alembic/ddl/__pycache__/base.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/alembic/ddl/__pycache__/impl.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/alembic/ddl/__pycache__/mssql.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/alembic/ddl/__pycache__/mysql.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/alembic/ddl/__pycache__/oracle.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/alembic/ddl/__pycache__/postgresql.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/alembic/ddl/__pycache__/sqlite.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/alembic/operations/__pycache__/__init__.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/alembic/operations/__pycache__/base.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/alembic/operations/__pycache__/batch.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/alembic/operations/__pycache__/ops.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/alembic/operations/__pycache__/schemaobj.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/alembic/operations/__pycache__/toimpl.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/alembic/runtime/__pycache__/__init__.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/alembic/runtime/__pycache__/environment.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/alembic/runtime/__pycache__/migration.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/alembic/script/__pycache__/__init__.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/alembic/script/__pycache__/base.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/alembic/script/__pycache__/revision.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/alembic/script/__pycache__/write_hooks.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/alembic/templates/async/__pycache__/env.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/alembic/templates/generic/__pycache__/env.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/alembic/templates/multidb/__pycache__/env.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/alembic/testing/__pycache__/__init__.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/alembic/testing/__pycache__/assertions.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/alembic/testing/__pycache__/env.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/alembic/testing/__pycache__/fixtures.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/alembic/testing/__pycache__/requirements.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/alembic/testing/__pycache__/schemacompare.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/alembic/testing/__pycache__/util.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/alembic/testing/__pycache__/warnings.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/alembic/testing/plugin/__pycache__/__init__.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/alembic/testing/plugin/__pycache__/bootstrap.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/alembic/testing/suite/__pycache__/__init__.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/alembic/testing/suite/__pycache__/_autogen_fixtures.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/alembic/testing/suite/__pycache__/test_autogen_comments.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/alembic/testing/suite/__pycache__/test_autogen_computed.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/alembic/testing/suite/__pycache__/test_autogen_diffs.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/alembic/testing/suite/__pycache__/test_autogen_fks.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/alembic/testing/suite/__pycache__/test_autogen_identity.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/alembic/testing/suite/__pycache__/test_environment.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/alembic/testing/suite/__pycache__/test_op.cpython-312.pyc is excluded by !**/*.pyc
  • .venv/lib/python3.12/site-packages/alembic/util/__pycache__/__init__.cpython-312.pyc is excluded by !**/*.pyc
📒 Files selected for processing (60)
  • .venv/bin/Activate.ps1 (1 hunks)
  • .venv/bin/activate (1 hunks)
  • .venv/bin/activate.csh (1 hunks)
  • .venv/bin/activate.fish (1 hunks)
  • .venv/bin/alembic (1 hunks)
  • .venv/bin/dotenv (1 hunks)
  • .venv/bin/fastapi (1 hunks)
  • .venv/bin/httpx (1 hunks)
  • .venv/bin/mako-render (1 hunks)
  • .venv/bin/nltk (1 hunks)
  • .venv/bin/normalizer (1 hunks)
  • .venv/bin/pip (1 hunks)
  • .venv/bin/pip3 (1 hunks)
  • .venv/bin/pip3.12 (1 hunks)
  • .venv/bin/py.test (1 hunks)
  • .venv/bin/pytest (1 hunks)
  • .venv/bin/tqdm (1 hunks)
  • .venv/bin/uvicorn (1 hunks)
  • .venv/include/site/python3.12/greenlet/greenlet.h (1 hunks)
  • .venv/lib/python3.12/site-packages/SQLAlchemy-2.0.31.dist-info/INSTALLER (1 hunks)
  • .venv/lib/python3.12/site-packages/SQLAlchemy-2.0.31.dist-info/LICENSE (1 hunks)
  • .venv/lib/python3.12/site-packages/SQLAlchemy-2.0.31.dist-info/METADATA (1 hunks)
  • .venv/lib/python3.12/site-packages/SQLAlchemy-2.0.31.dist-info/RECORD (1 hunks)
  • .venv/lib/python3.12/site-packages/SQLAlchemy-2.0.31.dist-info/WHEEL (1 hunks)
  • .venv/lib/python3.12/site-packages/SQLAlchemy-2.0.31.dist-info/top_level.txt (1 hunks)
  • .venv/lib/python3.12/site-packages/_pytest/__init__.py (1 hunks)
  • .venv/lib/python3.12/site-packages/_pytest/_argcomplete.py (1 hunks)
  • .venv/lib/python3.12/site-packages/_pytest/_code/__init__.py (1 hunks)
  • .venv/lib/python3.12/site-packages/_pytest/_code/code.py (1 hunks)
  • .venv/lib/python3.12/site-packages/_pytest/_code/source.py (1 hunks)
  • .venv/lib/python3.12/site-packages/_pytest/_io/__init__.py (1 hunks)
  • .venv/lib/python3.12/site-packages/_pytest/_io/pprint.py (1 hunks)
  • .venv/lib/python3.12/site-packages/_pytest/_io/saferepr.py (1 hunks)
  • .venv/lib/python3.12/site-packages/_pytest/_io/terminalwriter.py (1 hunks)
  • .venv/lib/python3.12/site-packages/_pytest/_io/wcwidth.py (1 hunks)
  • .venv/lib/python3.12/site-packages/_pytest/_py/error.py (1 hunks)
  • .venv/lib/python3.12/site-packages/_pytest/_py/path.py (1 hunks)
  • .venv/lib/python3.12/site-packages/_pytest/_version.py (1 hunks)
  • .venv/lib/python3.12/site-packages/_pytest/assertion/__init__.py (1 hunks)
  • .venv/lib/python3.12/site-packages/_pytest/assertion/rewrite.py (1 hunks)
  • .venv/lib/python3.12/site-packages/_pytest/assertion/truncate.py (1 hunks)
  • .venv/lib/python3.12/site-packages/_pytest/assertion/util.py (1 hunks)
  • .venv/lib/python3.12/site-packages/_pytest/cacheprovider.py (1 hunks)
  • .venv/lib/python3.12/site-packages/_pytest/capture.py (1 hunks)
  • .venv/lib/python3.12/site-packages/_pytest/compat.py (1 hunks)
  • .venv/lib/python3.12/site-packages/_pytest/config/__init__.py (1 hunks)
  • .venv/lib/python3.12/site-packages/_pytest/config/argparsing.py (1 hunks)
  • .venv/lib/python3.12/site-packages/_pytest/config/compat.py (1 hunks)
  • .venv/lib/python3.12/site-packages/_pytest/config/exceptions.py (1 hunks)
  • .venv/lib/python3.12/site-packages/_pytest/config/findpaths.py (1 hunks)
  • .venv/lib/python3.12/site-packages/_pytest/debugging.py (1 hunks)
  • .venv/lib/python3.12/site-packages/_pytest/deprecated.py (1 hunks)
  • .venv/lib/python3.12/site-packages/_pytest/doctest.py (1 hunks)
  • .venv/lib/python3.12/site-packages/_pytest/faulthandler.py (1 hunks)
  • .venv/lib/python3.12/site-packages/_pytest/fixtures.py (1 hunks)
  • .venv/lib/python3.12/site-packages/_pytest/freeze_support.py (1 hunks)
  • .venv/lib/python3.12/site-packages/_pytest/helpconfig.py (1 hunks)
  • .venv/lib/python3.12/site-packages/_pytest/hookspec.py (1 hunks)
  • .venv/lib/python3.12/site-packages/_pytest/junitxml.py (1 hunks)
  • .venv/lib/python3.12/site-packages/_pytest/legacypath.py (1 hunks)
✅ Files skipped from review due to trivial changes (8)
  • .venv/bin/httpx
  • .venv/bin/pip3
  • .venv/lib/python3.12/site-packages/SQLAlchemy-2.0.31.dist-info/LICENSE
  • .venv/lib/python3.12/site-packages/SQLAlchemy-2.0.31.dist-info/top_level.txt
  • .venv/bin/pytest
  • .venv/lib/python3.12/site-packages/SQLAlchemy-2.0.31.dist-info/WHEEL
  • .venv/lib/python3.12/site-packages/SQLAlchemy-2.0.31.dist-info/INSTALLER
  • .venv/bin/pip
🧰 Additional context used
🪛 Clang (14.0.6)
.venv/include/site/python3.12/greenlet/greenlet.h

[error] 9-9: 'Python.h' file not found

(clang-diagnostic-error)

🪛 Ruff (0.14.7)
.venv/lib/python3.12/site-packages/_pytest/_argcomplete.py

80-80: Unused method argument: kwargs

(ARG002)

.venv/lib/python3.12/site-packages/_pytest/_io/saferepr.py

11-11: Do not catch blind exception: BaseException

(BLE001)


20-20: Do not catch blind exception: BaseException

(BLE001)


65-65: Do not catch blind exception: BaseException

(BLE001)


71-71: Unused method argument: level

(ARG002)


76-76: Do not catch blind exception: BaseException

(BLE001)


91-91: Do not catch blind exception: Exception

(BLE001)


129-129: Do not catch blind exception: Exception

(BLE001)

.venv/lib/python3.12/site-packages/_pytest/_py/error.py

58-58: Mutable class attributes should be annotated with typing.ClassVar

(RUF012)


98-98: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


103-103: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)

.venv/lib/python3.12/site-packages/_pytest/_io/pprint.py

83-83: Avoid specifying long messages outside the exception class

(TRY003)


85-85: Avoid specifying long messages outside the exception class

(TRY003)


87-87: Avoid specifying long messages outside the exception class

(TRY003)


155-155: Mutable class attributes should be annotated with typing.ClassVar

(RUF012)


255-255: Unused method argument: context

(ARG002)


314-314: Unused method argument: context

(ARG002)


397-397: Unused method argument: allowance

(ARG002)


421-421: Unused method argument: allowance

(ARG002)


458-458: Unused method argument: allowance

(ARG002)

.venv/lib/python3.12/site-packages/_pytest/config/compat.py

28-31: Avoid specifying long messages outside the exception class

(TRY003)

.venv/lib/python3.12/site-packages/_pytest/doctest.py

488-488: No explicit stacklevel keyword argument found

Set stacklevel=2

(B028)


658-658: zip() without an explicit strict= parameter

Add explicit value for parameter strict=

(B905)

.venv/lib/python3.12/site-packages/_pytest/legacypath.py

398-398: Unused function argument: name

(ARG001)


407-407: Avoid specifying long messages outside the exception class

(TRY003)

.venv/lib/python3.12/site-packages/_pytest/faulthandler.py

59-59: Abstract raise to an inner function

(TRY301)


60-60: Consider moving this statement to an else block

(TRY300)

.venv/lib/python3.12/site-packages/_pytest/cacheprovider.py

147-147: Avoid specifying long messages outside the exception class

(TRY003)


405-405: Unused method argument: session

(ARG002)


573-573: Unused function argument: session

(ARG001)

.venv/lib/python3.12/site-packages/_pytest/_code/__init__.py

14-24: __all__ is not sorted

Apply an isort-style sorting to __all__

(RUF022)

.venv/lib/python3.12/site-packages/_pytest/_code/source.py

60-60: Avoid specifying long messages outside the exception class

(TRY003)


99-99: Avoid specifying long messages outside the exception class

(TRY003)


100-100: Unpacked variable ast is never used

Prefix it with an underscore or any other dummy variable pattern

(RUF059)


121-121: Do not catch blind exception: Exception

(BLE001)


135-135: Using hasattr(x, "__call__") to test if x is callable is unreliable. Use callable(x) for consistent results.

(B004)


138-138: Avoid specifying long messages outside the exception class

(TRY003)


174-174: Unused function argument: assertion

(ARG001)


207-208: try-except-pass detected, consider logging the exception

(S110)


207-207: Do not catch blind exception: Exception

(BLE001)

.venv/lib/python3.12/site-packages/_pytest/config/findpaths.py

73-73: Avoid specifying long messages outside the exception class

(TRY003)


219-221: Avoid specifying long messages outside the exception class

(TRY003)

.venv/lib/python3.12/site-packages/_pytest/compat.py

229-229: Loop control variable i not used within loop body

Rename unused i to _i

(B007)


244-246: Avoid specifying long messages outside the exception class

(TRY003)


259-259: Do not catch blind exception: Exception

(BLE001)


294-294: Do not catch blind exception: Exception

(BLE001)


351-351: Do not assert False (python -O removes these calls), raise AssertionError()

Replace assert False

(B011)

.venv/lib/python3.12/site-packages/_pytest/junitxml.py

72-72: Loop control variable kr not used within loop body

Rename unused kr to _kr

(B007)


190-190: Unused method argument: report

(ARG002)


195-195: Unused method argument: report

(ARG002)


372-372: Unused function argument: value

(ARG001)

.venv/lib/python3.12/site-packages/_pytest/_io/terminalwriter.py

45-66: Mutable class attributes should be annotated with typing.ClassVar

(RUF012)


103-103: Avoid specifying long messages outside the exception class

(TRY003)


187-189: Avoid specifying long messages outside the exception class

(TRY003)


194-194: zip() without an explicit strict= parameter

Add explicit value for parameter strict=

(B905)

.venv/lib/python3.12/site-packages/_pytest/debugging.py

41-43: Avoid specifying long messages outside the exception class

(TRY003)


72-72: Import for pdb found

(T100)


105-105: Mutable class attributes should be annotated with typing.ClassVar

(RUF012)


118-118: Import for pdb found

(T100)


142-144: Avoid specifying long messages outside the exception class

(TRY003)


146-146: Import for pdb found

(T100)


240-240: Unused class method argument: args

(ARG003)

.venv/lib/python3.12/site-packages/_pytest/assertion/truncate.py

21-21: Unused function argument: max_length

(ARG001)


106-106: Loop control variable iterated_index not used within loop body

(B007)

.venv/lib/python3.12/site-packages/_pytest/assertion/rewrite.py

45-45: Unused noqa directive (non-enabled: F401)

Remove unused noqa directive

(RUF100)


60-60: Parenthesize a and b expressions when chaining and and or together, to make the precedence clear

Parenthesize the and subexpression

(RUF021)


97-97: Unused method argument: target

(ARG002)


134-134: Unused method argument: spec

(ARG002)


178-178: Use of exec detected

(S102)


356-356: Unused lambda argument: x

(ARG005)


394-394: Deserialization with the marshal module is possibly dangerous

(S302)


395-395: Do not catch blind exception: Exception

(BLE001)


469-469: Do not catch blind exception: Exception

(BLE001)


474-474: Parenthesize a and b expressions when chaining and and or together, to make the precedence clear

Parenthesize the and subexpression

(RUF021)


484-484: Loop control variable i not used within loop body

(B007)


484-484: Loop control variable expl not used within loop body

(B007)


484-484: zip() without an explicit strict= parameter

Add explicit value for parameter strict=

(B905)


487-487: Do not catch blind exception: Exception

(BLE001)


753-753: Loop control variable i not used within loop body

Rename unused i to _i

(B007)


1119-1119: zip() without an explicit strict= parameter

Add explicit value for parameter strict=

(B905)

.venv/lib/python3.12/site-packages/_pytest/_code/code.py

97-97: Abstract raise to an inner function

(TRY301)


97-97: Avoid specifying long messages outside the exception class

(TRY003)


98-98: Consider moving this statement to an else block

(TRY300)


171-171: Use of possibly insecure function; consider using ast.literal_eval

(S307)


292-293: try-except-pass detected, consider logging the exception

(S110)


292-292: Do not catch blind exception: Exception

(BLE001)


306-306: Do not catch blind exception: BaseException

(BLE001)


824-824: Do not catch blind exception: BaseException

(BLE001)


829-829: Do not catch blind exception: BaseException

(BLE001)


945-945: Parenthesize a and b expressions when chaining and and or together, to make the precedence clear

Parenthesize the and subexpression

(RUF021)


1016-1016: Do not catch blind exception: Exception

(BLE001)


1182-1183: Parenthesize a and b expressions when chaining and and or together, to make the precedence clear

Parenthesize the and subexpression

(RUF021)


1361-1361: Parenthesize a and b expressions when chaining and and or together, to make the precedence clear

Parenthesize the and subexpression

(RUF021)

.venv/lib/python3.12/site-packages/_pytest/fixtures.py

296-296: FuncFixtureInfo.__slots__ is not sorted

Apply a natural sort to FuncFixtureInfo.__slots__

(RUF023)


423-425: Avoid specifying long messages outside the exception class

(TRY003)


432-432: Avoid specifying long messages outside the exception class

(TRY003)


448-448: Avoid specifying long messages outside the exception class

(TRY003)


457-457: Avoid specifying long messages outside the exception class

(TRY003)


807-807: Loop control variable i not used within loop body

Rename unused i to _i

(B007)


879-879: Avoid specifying long messages outside the exception class

(TRY003)


916-919: Avoid specifying long messages outside the exception class

(TRY003)


1010-1010: Do not catch blind exception: BaseException

(BLE001)


1150-1150: Unused function argument: args

(ARG001)


1150-1150: Unused function argument: kwargs

(ARG001)


1178-1178: Avoid specifying long messages outside the exception class

(TRY003)


1181-1183: Avoid specifying long messages outside the exception class

(TRY003)


1868-1868: Loop control variable argname not used within loop body

(B007)


1889-1889: Loop control variable baseid not used within loop body

Rename unused baseid to _baseid

(B007)

.venv/lib/python3.12/site-packages/_pytest/helpconfig.py

40-40: Unused method argument: values

(ARG002)


40-40: Unused method argument: option_string

(ARG002)


182-182: Unpacked variable default is never used

Prefix it with an underscore or any other dummy variable pattern

(RUF059)


186-186: Avoid specifying long messages outside the exception class

(TRY003)

.venv/lib/python3.12/site-packages/_pytest/config/argparsing.py

87-87: Loop control variable i not used within loop body

(B007)


292-292: Avoid specifying long messages outside the exception class

(TRY003)


398-398: Avoid specifying long messages outside the exception class

(TRY003)

.venv/lib/python3.12/site-packages/_pytest/config/__init__.py

208-208: Consider moving this statement to an else block

(TRY300)


228-228: Avoid specifying long messages outside the exception class

(TRY003)


239-239: Avoid specifying long messages outside the exception class

(TRY003)


349-349: Consider moving this statement to an else block

(TRY300)


446-447: try-except-pass detected, consider logging the exception

(S110)


446-446: Do not catch blind exception: Exception

(BLE001)


499-499: No explicit stacklevel keyword argument found

Set stacklevel=2

(B028)


735-739: Avoid specifying long messages outside the exception class

(TRY003)


1149-1149: Unused method argument: pluginmanager

(ARG002)


1222-1222: Unpacked variable args_source is never used

Prefix it with an underscore or any other dummy variable pattern

(RUF059)


1275-1275: Unpacked variable unknown_args is never used

Prefix it with an underscore or any other dummy variable pattern

(RUF059)


1442-1444: Avoid specifying long messages outside the exception class

(TRY003)


1597-1597: Unused method argument: name

(ARG002)


1603-1603: Unpacked variable description is never used

Prefix it with an underscore or any other dummy variable pattern

(RUF059)


1605-1605: Avoid specifying long messages outside the exception class

(TRY003)


1679-1681: Avoid specifying long messages outside the exception class

(TRY003)


1700-1700: Abstract raise to an inner function

(TRY301)


1701-1701: Consider moving this statement to an else block

(TRY300)


1709-1709: Avoid specifying long messages outside the exception class

(TRY003)


1711-1711: Unused method argument: path

(ARG002)


1715-1715: Unused method argument: path

(ARG002)


1823-1823: Do not assert False (python -O removes these calls), raise AssertionError()

Replace assert False

(B011)


1869-1869: Avoid specifying long messages outside the exception class

(TRY003)


1922-1922: Do not catch blind exception: Exception

(BLE001)


1934-1934: Abstract raise to an inner function

(TRY301)


1934-1934: Avoid specifying long messages outside the exception class

(TRY003)


1962-1962: Avoid specifying long messages outside the exception class

(TRY003)

.venv/lib/python3.12/site-packages/_pytest/assertion/util.py

145-145: Do not catch blind exception: Exception

(BLE001)


225-225: Do not catch blind exception: Exception

(BLE001)


358-358: Unused function argument: verbose

(ARG001)


419-419: Unused function argument: verbose

(ARG001)


431-431: Unused function argument: verbose

(ARG001)


443-443: Unused function argument: verbose

(ARG001)


455-455: Unused function argument: verbose

(ARG001)


464-464: Unused function argument: verbose

(ARG001)


544-544: Do not call getattr with a constant attribute value. It is not any safer than normal property access.

Replace getattr with attribute access

(B009)


548-548: Do not assert False (python -O removes these calls), raise AssertionError()

Replace assert False

(B011)

.venv/lib/python3.12/site-packages/_pytest/capture.py

76-76: Unused noqa directive (non-enabled: F401)

Remove unused noqa directive

(RUF100)


206-206: Unused method argument: size

(ARG002)


207-209: Avoid specifying long messages outside the exception class

(TRY003)


216-216: Unused method argument: hint

(ARG002)


217-219: Avoid specifying long messages outside the exception class

(TRY003)


225-225: Avoid specifying long messages outside the exception class

(TRY003)


228-228: Avoid specifying long messages outside the exception class

(TRY003)


239-239: Unused method argument: offset

(ARG002)


239-239: Unused method argument: whence

(ARG002)


240-240: Avoid specifying long messages outside the exception class

(TRY003)


246-246: Avoid specifying long messages outside the exception class

(TRY003)


248-248: Unused method argument: size

(ARG002)


249-249: Avoid specifying long messages outside the exception class

(TRY003)


251-251: Unused method argument: data

(ARG002)


252-252: Avoid specifying long messages outside the exception class

(TRY003)


254-254: Unused method argument: lines

(ARG002)


255-255: Avoid specifying long messages outside the exception class

(TRY003)


359-359: Parenthesize a and b expressions when chaining and and or together, to make the precedence clear

Parenthesize the and subexpression

(RUF021)


368-368: Parenthesize a and b expressions when chaining and and or together, to make the precedence clear

Parenthesize the and subexpression

(RUF021)


598-598: Unused noqa directive (non-enabled: PYI024)

Remove unused noqa directive

(RUF100)


670-670: Avoid specifying long messages outside the exception class

(TRY003)


701-701: Avoid specifying long messages outside the exception class

(TRY003)

.venv/lib/python3.12/site-packages/_pytest/_py/path.py

86-86: Avoid specifying long messages outside the exception class

(TRY003)


144-144: Using hasattr(x, "__call__") to test if x is callable is unreliable. Use callable(x) for consistent results.

Replace with callable()

(B004)


145-145: Unused lambda argument: path

(ARG005)


293-296: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


293-296: Avoid specifying long messages outside the exception class

(TRY003)


438-438: Avoid specifying long messages outside the exception class

(TRY003)


478-478: Consider moving this statement to an else block

(TRY300)


512-512: zip() without an explicit strict= parameter

Add explicit value for parameter strict=

(B905)


544-544: Using hasattr(x, "__call__") to test if x is callable is unreliable. Use callable(x) for consistent results.

Replace with callable()

(B004)


631-631: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


631-631: Avoid specifying long messages outside the exception class

(TRY003)


657-657: Unpacked variable basename is never used

Prefix it with an underscore or any other dummy variable pattern

(RUF059)


803-803: Mutable class attributes should be annotated with typing.ClassVar

(RUF012)


920-920: Avoid specifying long messages outside the exception class

(TRY003)


977-977: Do not catch blind exception: Exception

(BLE001)


1041-1041: Avoid specifying long messages outside the exception class

(TRY003)


1101-1101: Avoid specifying long messages outside the exception class

(TRY003)


1155-1155: Use of exec detected

(S102)


1171-1171: subprocess call: check for execution of untrusted input

(S603)


1406-1407: try-except-pass detected, consider logging the exception

(S110)


1406-1406: Do not catch blind exception: Exception

(BLE001)


1413-1414: try-except-pass detected, consider logging the exception

(S110)


1413-1413: Do not catch blind exception: Exception

(BLE001)

.venv/lib/python3.12/site-packages/_pytest/hookspec.py

22-22: Import for pdb found

(T100)

🔇 Additional comments (2)
.venv/lib/python3.12/site-packages/SQLAlchemy-2.0.31.dist-info/METADATA (1)

1-3: PR objectives mismatch: expected time_tracker.py not found.

The PR description states this adds app/core/time_tracker.py with start_timer and finish_timer functions, but the files provided for review are virtual environment artifacts unrelated to that feature. Please ensure the actual feature code is included in this PR and remove the .venv/ files.

.venv/lib/python3.12/site-packages/_pytest/_py/path.py (1)

1-1477: Do not commit the .venv/ directory to version control.

This file is part of the pytest library installed in your virtual environment. Committing .venv/ is a well-known anti-pattern that causes:

  1. Repository bloat — Virtual environments contain thousands of files and can be hundreds of MB
  2. Platform incompatibility — Compiled extensions (.so, .pyd) are platform-specific
  3. Dependency management issues — Updates and security patches become manual operations

Add .venv/ to your .gitignore and remove this directory from the repository. Dependencies should be managed via pip install -r requirements.txt.

Additionally, verify that the PR includes the intended changes mentioned in the objectives (e.g., app/core/time_tracker.py with start_timer and finish_timer functions).

Comment on lines 1 to 8
#!/home/repositories/LumintelAnalytics/ChainReport-API/.venv/bin/python3
# -*- coding: utf-8 -*-
import re
import sys
from mako.cmd import cmdline
if __name__ == '__main__':
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
sys.exit(cmdline())
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Do not commit virtual environment files to version control.

This file is auto-generated by pip when installing packages and contains a hardcoded absolute path (/home/repositories/LumintelAnalytics/ChainReport-API/.venv/bin/python3) that will not work on other developers' machines or CI/CD environments.

The entire .venv/ directory should be excluded from version control. Add it to .gitignore:

+# Virtual environment
+.venv/
+venv/
+ENV/

Then remove the committed .venv/ directory:

git rm -r --cached .venv/
🤖 Prompt for AI Agents
.venv/bin/mako-render lines 1-8: this is a generated virtualenv script
containing an absolute interpreter path and should not be committed; remove the
entire .venv directory from the repo and add .venv/ to .gitignore, then run git
rm -r --cached .venv/ (or delete the folder and commit the removal) and commit
the updated .gitignore so the virtual environment and its generated scripts are
no longer tracked.

Comment on lines 1 to 164
/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */

/* Greenlet object interface */

#ifndef Py_GREENLETOBJECT_H
#define Py_GREENLETOBJECT_H


#include <Python.h>

#ifdef __cplusplus
extern "C" {
#endif

/* This is deprecated and undocumented. It does not change. */
#define GREENLET_VERSION "1.0.0"

#ifndef GREENLET_MODULE
#define implementation_ptr_t void*
#endif

typedef struct _greenlet {
PyObject_HEAD
PyObject* weakreflist;
PyObject* dict;
implementation_ptr_t pimpl;
} PyGreenlet;

#define PyGreenlet_Check(op) (op && PyObject_TypeCheck(op, &PyGreenlet_Type))


/* C API functions */

/* Total number of symbols that are exported */
#define PyGreenlet_API_pointers 12

#define PyGreenlet_Type_NUM 0
#define PyExc_GreenletError_NUM 1
#define PyExc_GreenletExit_NUM 2

#define PyGreenlet_New_NUM 3
#define PyGreenlet_GetCurrent_NUM 4
#define PyGreenlet_Throw_NUM 5
#define PyGreenlet_Switch_NUM 6
#define PyGreenlet_SetParent_NUM 7

#define PyGreenlet_MAIN_NUM 8
#define PyGreenlet_STARTED_NUM 9
#define PyGreenlet_ACTIVE_NUM 10
#define PyGreenlet_GET_PARENT_NUM 11

#ifndef GREENLET_MODULE
/* This section is used by modules that uses the greenlet C API */
static void** _PyGreenlet_API = NULL;

# define PyGreenlet_Type \
(*(PyTypeObject*)_PyGreenlet_API[PyGreenlet_Type_NUM])

# define PyExc_GreenletError \
((PyObject*)_PyGreenlet_API[PyExc_GreenletError_NUM])

# define PyExc_GreenletExit \
((PyObject*)_PyGreenlet_API[PyExc_GreenletExit_NUM])

/*
* PyGreenlet_New(PyObject *args)
*
* greenlet.greenlet(run, parent=None)
*/
# define PyGreenlet_New \
(*(PyGreenlet * (*)(PyObject * run, PyGreenlet * parent)) \
_PyGreenlet_API[PyGreenlet_New_NUM])

/*
* PyGreenlet_GetCurrent(void)
*
* greenlet.getcurrent()
*/
# define PyGreenlet_GetCurrent \
(*(PyGreenlet * (*)(void)) _PyGreenlet_API[PyGreenlet_GetCurrent_NUM])

/*
* PyGreenlet_Throw(
* PyGreenlet *greenlet,
* PyObject *typ,
* PyObject *val,
* PyObject *tb)
*
* g.throw(...)
*/
# define PyGreenlet_Throw \
(*(PyObject * (*)(PyGreenlet * self, \
PyObject * typ, \
PyObject * val, \
PyObject * tb)) \
_PyGreenlet_API[PyGreenlet_Throw_NUM])

/*
* PyGreenlet_Switch(PyGreenlet *greenlet, PyObject *args)
*
* g.switch(*args, **kwargs)
*/
# define PyGreenlet_Switch \
(*(PyObject * \
(*)(PyGreenlet * greenlet, PyObject * args, PyObject * kwargs)) \
_PyGreenlet_API[PyGreenlet_Switch_NUM])

/*
* PyGreenlet_SetParent(PyObject *greenlet, PyObject *new_parent)
*
* g.parent = new_parent
*/
# define PyGreenlet_SetParent \
(*(int (*)(PyGreenlet * greenlet, PyGreenlet * nparent)) \
_PyGreenlet_API[PyGreenlet_SetParent_NUM])

/*
* PyGreenlet_GetParent(PyObject* greenlet)
*
* return greenlet.parent;
*
* This could return NULL even if there is no exception active.
* If it does not return NULL, you are responsible for decrementing the
* reference count.
*/
# define PyGreenlet_GetParent \
(*(PyGreenlet* (*)(PyGreenlet*)) \
_PyGreenlet_API[PyGreenlet_GET_PARENT_NUM])

/*
* deprecated, undocumented alias.
*/
# define PyGreenlet_GET_PARENT PyGreenlet_GetParent

# define PyGreenlet_MAIN \
(*(int (*)(PyGreenlet*)) \
_PyGreenlet_API[PyGreenlet_MAIN_NUM])

# define PyGreenlet_STARTED \
(*(int (*)(PyGreenlet*)) \
_PyGreenlet_API[PyGreenlet_STARTED_NUM])

# define PyGreenlet_ACTIVE \
(*(int (*)(PyGreenlet*)) \
_PyGreenlet_API[PyGreenlet_ACTIVE_NUM])




/* Macro that imports greenlet and initializes C API */
/* NOTE: This has actually moved to ``greenlet._greenlet._C_API``, but we
keep the older definition to be sure older code that might have a copy of
the header still works. */
# define PyGreenlet_Import() \
{ \
_PyGreenlet_API = (void**)PyCapsule_Import("greenlet._C_API", 0); \
}

#endif /* GREENLET_MODULE */

#ifdef __cplusplus
}
#endif
#endif /* !Py_GREENLETOBJECT_H */
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Do not commit virtual environment files to version control.

This file is part of the .venv/ directory, which is a Python virtual environment containing installed dependencies. Virtual environment directories should never be committed to version control because:

  1. They are auto-generated and can be recreated from requirements.txt or pyproject.toml
  2. They bloat the repository significantly (can be hundreds of MBs)
  3. They contain platform-specific binaries that may not work across different systems
  4. They create unnecessary merge conflicts

Additionally, this file (greenlet C header) is unrelated to the stated PR objective of adding time_tracker.py in app/core/.

Recommended actions:

  1. Remove the entire .venv/ directory from the commit
  2. Add .venv/ to your .gitignore file:
+# Virtual environments
+.venv/
+venv/
+ENV/
+env/
  1. If you accidentally committed .venv/, remove it from git tracking:
git rm -r --cached .venv/

Regarding the static analysis hint about Python.h not found — this is expected for header files meant to be used in build environments with Python development headers installed. However, since this file should not be committed at all, this warning is moot.

🧰 Tools
🪛 Clang (14.0.6)

[error] 9-9: 'Python.h' file not found

(clang-diagnostic-error)

🤖 Prompt for AI Agents
.venv/include/site/python3.12/greenlet/greenlet.h lines 1-164: This file is
inside a committed .venv virtual environment and should not be in the repo;
remove the entire .venv directory from the commit, add “.venv/” to .gitignore,
stop tracking any already committed files with git rm -r --cached .venv/ (then
commit the removal), and ensure your PR only contains the intended change
(app/core/time_tracker.py) before pushing the corrected commit(s).

Comment on lines 1 to 117
"""Allow bash-completion for argparse with argcomplete if installed.

Needs argcomplete>=0.5.6 for python 3.2/3.3 (older versions fail
to find the magic string, so _ARGCOMPLETE env. var is never set, and
this does not need special code).

Function try_argcomplete(parser) should be called directly before
the call to ArgumentParser.parse_args().

The filescompleter is what you normally would use on the positional
arguments specification, in order to get "dirname/" after "dirn<TAB>"
instead of the default "dirname ":

optparser.add_argument(Config._file_or_dir, nargs='*').completer=filescompleter

Other, application specific, completers should go in the file
doing the add_argument calls as they need to be specified as .completer
attributes as well. (If argcomplete is not installed, the function the
attribute points to will not be used).

SPEEDUP
=======

The generic argcomplete script for bash-completion
(/etc/bash_completion.d/python-argcomplete.sh)
uses a python program to determine startup script generated by pip.
You can speed up completion somewhat by changing this script to include
# PYTHON_ARGCOMPLETE_OK
so the python-argcomplete-check-easy-install-script does not
need to be called to find the entry point of the code and see if that is
marked with PYTHON_ARGCOMPLETE_OK.

INSTALL/DEBUGGING
=================

To include this support in another application that has setup.py generated
scripts:

- Add the line:
# PYTHON_ARGCOMPLETE_OK
near the top of the main python entry point.

- Include in the file calling parse_args():
from _argcomplete import try_argcomplete, filescompleter
Call try_argcomplete just before parse_args(), and optionally add
filescompleter to the positional arguments' add_argument().

If things do not work right away:

- Switch on argcomplete debugging with (also helpful when doing custom
completers):
export _ARC_DEBUG=1

- Run:
python-argcomplete-check-easy-install-script $(which appname)
echo $?
will echo 0 if the magic line has been found, 1 if not.

- Sometimes it helps to find early on errors using:
_ARGCOMPLETE=1 _ARC_DEBUG=1 appname
which should throw a KeyError: 'COMPLINE' (which is properly set by the
global argcomplete script).
"""

import argparse
from glob import glob
import os
import sys
from typing import Any
from typing import List
from typing import Optional


class FastFilesCompleter:
"""Fast file completer class."""

def __init__(self, directories: bool = True) -> None:
self.directories = directories

def __call__(self, prefix: str, **kwargs: Any) -> List[str]:
# Only called on non option completions.
if os.sep in prefix[1:]:
prefix_dir = len(os.path.dirname(prefix) + os.sep)
else:
prefix_dir = 0
completion = []
globbed = []
if "*" not in prefix and "?" not in prefix:
# We are on unix, otherwise no bash.
if not prefix or prefix[-1] == os.sep:
globbed.extend(glob(prefix + ".*"))
prefix += "*"
globbed.extend(glob(prefix))
for x in sorted(globbed):
if os.path.isdir(x):
x += "/"
# Append stripping the prefix (like bash, not like compgen).
completion.append(x[prefix_dir:])
return completion


if os.environ.get("_ARGCOMPLETE"):
try:
import argcomplete.completers
except ImportError:
sys.exit(-1)
filescompleter: Optional[FastFilesCompleter] = FastFilesCompleter()

def try_argcomplete(parser: argparse.ArgumentParser) -> None:
argcomplete.autocomplete(parser, always_complete_options=False)

else:

def try_argcomplete(parser: argparse.ArgumentParser) -> None:
pass

filescompleter = None
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Do not commit virtual environment files to version control.

This file is located inside .venv/lib/python3.12/site-packages/, which is a Python virtual environment directory containing installed third-party packages. Virtual environments should never be committed to the repository because:

  1. They are machine-specific and platform-dependent
  2. They bloat the repository with thousands of generated files
  3. They can be regenerated from requirements.txt or pyproject.toml

Additionally, this file (_pytest/_argcomplete.py) is third-party pytest code, not the time_tracker.py mentioned in the PR description.

Recommended actions:

  1. Remove the .venv/ directory from this PR and from version control:
git rm -r --cached .venv/
  1. Add .venv/ to your .gitignore file:
+# Virtual environment
+.venv/
+venv/
+ENV/
  1. Ensure the actual app/core/time_tracker.py file mentioned in the PR description is included in this PR instead.
🧰 Tools
🪛 Ruff (0.14.7)

80-80: Unused method argument: kwargs

(ARG002)

🤖 Prompt for AI Agents
.venv/lib/python3.12/site-packages/_pytest/_argcomplete.py lines 1-117: The
virtual environment and third-party package file were accidentally committed;
remove the .venv directory from the repo, stop tracking it, and update
.gitignore. Run git rm -r --cached .venv/ to unstage and remove the venv from
version control, add an entry for .venv/ to .gitignore (or ensure it exists),
commit those changes, and then add the intended project file
app/core/time_tracker.py to the PR so only your project source (not installed
packages) is included.

Comment on lines 1 to 217
# mypy: allow-untyped-defs
import ast
from bisect import bisect_right
import inspect
import textwrap
import tokenize
import types
from typing import Iterable
from typing import Iterator
from typing import List
from typing import Optional
from typing import overload
from typing import Tuple
from typing import Union
import warnings


class Source:
"""An immutable object holding a source code fragment.

When using Source(...), the source lines are deindented.
"""

def __init__(self, obj: object = None) -> None:
if not obj:
self.lines: List[str] = []
elif isinstance(obj, Source):
self.lines = obj.lines
elif isinstance(obj, (tuple, list)):
self.lines = deindent(x.rstrip("\n") for x in obj)
elif isinstance(obj, str):
self.lines = deindent(obj.split("\n"))
else:
try:
rawcode = getrawcode(obj)
src = inspect.getsource(rawcode)
except TypeError:
src = inspect.getsource(obj) # type: ignore[arg-type]
self.lines = deindent(src.split("\n"))

def __eq__(self, other: object) -> bool:
if not isinstance(other, Source):
return NotImplemented
return self.lines == other.lines

# Ignore type because of https://github.com/python/mypy/issues/4266.
__hash__ = None # type: ignore

@overload
def __getitem__(self, key: int) -> str: ...

@overload
def __getitem__(self, key: slice) -> "Source": ...

def __getitem__(self, key: Union[int, slice]) -> Union[str, "Source"]:
if isinstance(key, int):
return self.lines[key]
else:
if key.step not in (None, 1):
raise IndexError("cannot slice a Source with a step")
newsource = Source()
newsource.lines = self.lines[key.start : key.stop]
return newsource

def __iter__(self) -> Iterator[str]:
return iter(self.lines)

def __len__(self) -> int:
return len(self.lines)

def strip(self) -> "Source":
"""Return new Source object with trailing and leading blank lines removed."""
start, end = 0, len(self)
while start < end and not self.lines[start].strip():
start += 1
while end > start and not self.lines[end - 1].strip():
end -= 1
source = Source()
source.lines[:] = self.lines[start:end]
return source

def indent(self, indent: str = " " * 4) -> "Source":
"""Return a copy of the source object with all lines indented by the
given indent-string."""
newsource = Source()
newsource.lines = [(indent + line) for line in self.lines]
return newsource

def getstatement(self, lineno: int) -> "Source":
"""Return Source statement which contains the given linenumber
(counted from 0)."""
start, end = self.getstatementrange(lineno)
return self[start:end]

def getstatementrange(self, lineno: int) -> Tuple[int, int]:
"""Return (start, end) tuple which spans the minimal statement region
which containing the given lineno."""
if not (0 <= lineno < len(self)):
raise IndexError("lineno out of range")
ast, start, end = getstatementrange_ast(lineno, self)
return start, end

def deindent(self) -> "Source":
"""Return a new Source object deindented."""
newsource = Source()
newsource.lines[:] = deindent(self.lines)
return newsource

def __str__(self) -> str:
return "\n".join(self.lines)


#
# helper functions
#


def findsource(obj) -> Tuple[Optional[Source], int]:
try:
sourcelines, lineno = inspect.findsource(obj)
except Exception:
return None, -1
source = Source()
source.lines = [line.rstrip() for line in sourcelines]
return source, lineno


def getrawcode(obj: object, trycall: bool = True) -> types.CodeType:
"""Return code object for given function."""
try:
return obj.__code__ # type: ignore[attr-defined,no-any-return]
except AttributeError:
pass
if trycall:
call = getattr(obj, "__call__", None)
if call and not isinstance(obj, type):
return getrawcode(call, trycall=False)
raise TypeError(f"could not get code object for {obj!r}")


def deindent(lines: Iterable[str]) -> List[str]:
return textwrap.dedent("\n".join(lines)).splitlines()


def get_statement_startend2(lineno: int, node: ast.AST) -> Tuple[int, Optional[int]]:
# Flatten all statements and except handlers into one lineno-list.
# AST's line numbers start indexing at 1.
values: List[int] = []
for x in ast.walk(node):
if isinstance(x, (ast.stmt, ast.ExceptHandler)):
# The lineno points to the class/def, so need to include the decorators.
if isinstance(x, (ast.ClassDef, ast.FunctionDef, ast.AsyncFunctionDef)):
for d in x.decorator_list:
values.append(d.lineno - 1)
values.append(x.lineno - 1)
for name in ("finalbody", "orelse"):
val: Optional[List[ast.stmt]] = getattr(x, name, None)
if val:
# Treat the finally/orelse part as its own statement.
values.append(val[0].lineno - 1 - 1)
values.sort()
insert_index = bisect_right(values, lineno)
start = values[insert_index - 1]
if insert_index >= len(values):
end = None
else:
end = values[insert_index]
return start, end


def getstatementrange_ast(
lineno: int,
source: Source,
assertion: bool = False,
astnode: Optional[ast.AST] = None,
) -> Tuple[ast.AST, int, int]:
if astnode is None:
content = str(source)
# See #4260:
# Don't produce duplicate warnings when compiling source to find AST.
with warnings.catch_warnings():
warnings.simplefilter("ignore")
astnode = ast.parse(content, "source", "exec")

start, end = get_statement_startend2(lineno, astnode)
# We need to correct the end:
# - ast-parsing strips comments
# - there might be empty lines
# - we might have lesser indented code blocks at the end
if end is None:
end = len(source.lines)

if end > start + 1:
# Make sure we don't span differently indented code blocks
# by using the BlockFinder helper used which inspect.getsource() uses itself.
block_finder = inspect.BlockFinder()
# If we start with an indented line, put blockfinder to "started" mode.
block_finder.started = (
bool(source.lines[start]) and source.lines[start][0].isspace()
)
it = ((x + "\n") for x in source.lines[start:end])
try:
for tok in tokenize.generate_tokens(lambda: next(it)):
block_finder.tokeneater(*tok)
except (inspect.EndOfBlock, IndentationError):
end = block_finder.last + start
except Exception:
pass

# The end might still point to a comment or empty line, correct it.
while end:
line = source.lines[end - 1].lstrip()
if line.startswith("#") or not line:
end -= 1
else:
break
return astnode, start, end
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Do not commit virtual environment files to the repository.

This file is located inside .venv/, which is the Python virtual environment directory containing installed third-party packages (pytest internals). Virtual environment files should never be committed to version control because:

  1. They bloat the repository with thousands of files
  2. They are platform/OS-specific and cause conflicts
  3. They can be recreated from requirements.txt or pyproject.toml

Add .venv/ to your .gitignore file and remove these files from the commit:

+# .gitignore
+.venv/
+venv/
+env/
+__pycache__/
+*.pyc

Then unstage and remove the virtual environment files:

git rm -r --cached .venv/
git commit -m "Remove accidentally committed virtual environment"

Additionally, the PR description mentions adding time_tracker.py in app/core/, but that file is not present in this review. Please ensure the correct files are being committed.

🧰 Tools
🪛 Ruff (0.14.7)

60-60: Avoid specifying long messages outside the exception class

(TRY003)


99-99: Avoid specifying long messages outside the exception class

(TRY003)


100-100: Unpacked variable ast is never used

Prefix it with an underscore or any other dummy variable pattern

(RUF059)


121-121: Do not catch blind exception: Exception

(BLE001)


135-135: Using hasattr(x, "__call__") to test if x is callable is unreliable. Use callable(x) for consistent results.

(B004)


138-138: Avoid specifying long messages outside the exception class

(TRY003)


174-174: Unused function argument: assertion

(ARG001)


207-208: try-except-pass detected, consider logging the exception

(S110)


207-207: Do not catch blind exception: Exception

(BLE001)

🤖 Prompt for AI Agents
In .venv/lib/python3.12/site-packages/_pytest/_code/source.py lines 1-217, you
accidentally committed virtual environment files; remove the .venv directory
from the repo by adding ".venv/" to .gitignore, untracking and deleting the
committed venv files from the index (git rm --cached -r .venv) and commit that
change, and then push; also verify the intended file app/core/time_tracker.py is
actually added to the branch and include it in the commit/PR if missing.

Comment on lines 1 to 8
from .terminalwriter import get_terminal_width
from .terminalwriter import TerminalWriter


__all__ = [
"TerminalWriter",
"get_terminal_width",
]
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: Virtual environment files should not be committed to the repository.

This file is located in .venv/lib/python3.12/site-packages/_pytest/ which is part of the Python virtual environment containing third-party packages. These files should be excluded from version control:

  1. Add .venv/ to .gitignore if not already present
  2. Remove all .venv/ files from this PR using git rm -r --cached .venv/
  3. The PR objective mentions creating time_tracker.py in app/core/, but that file is not included in this review

Virtual environment directories are machine-specific, bloat the repository, and should be regenerated via pip install -r requirements.txt on each developer's machine.

🤖 Prompt for AI Agents
.venv/lib/python3.12/site-packages/_pytest/__init__.py lines 1-8: this file is
part of a local virtual environment and must not be committed; fix by adding
.venv/ to .gitignore (if not present), removing the venv files from the repo
history/cache (for example remove the .venv directory from version control),
recommit the change so .venv files are no longer tracked, and ensure the
intended PR changes are present by adding the missing app/core/time_tracker.py
to this branch before pushing.

Comment on lines 1 to 700
# mypy: allow-untyped-defs
"""Report test results in JUnit-XML format, for use with Jenkins and build
integration servers.

Based on initial code from Ross Lawley.

Output conforms to
https://github.com/jenkinsci/xunit-plugin/blob/master/src/main/resources/org/jenkinsci/plugins/xunit/types/model/xsd/junit-10.xsd
"""

from datetime import datetime
import functools
import os
import platform
import re
from typing import Callable
from typing import Dict
from typing import List
from typing import Match
from typing import Optional
from typing import Tuple
from typing import Union
import xml.etree.ElementTree as ET

from _pytest import nodes
from _pytest import timing
from _pytest._code.code import ExceptionRepr
from _pytest._code.code import ReprFileLocation
from _pytest.config import Config
from _pytest.config import filename_arg
from _pytest.config.argparsing import Parser
from _pytest.fixtures import FixtureRequest
from _pytest.reports import TestReport
from _pytest.stash import StashKey
from _pytest.terminal import TerminalReporter
import pytest


xml_key = StashKey["LogXML"]()


def bin_xml_escape(arg: object) -> str:
r"""Visually escape invalid XML characters.

For example, transforms
'hello\aworld\b'
into
'hello#x07world#x08'
Note that the #xABs are *not* XML escapes - missing the ampersand &#xAB.
The idea is to escape visually for the user rather than for XML itself.
"""

def repl(matchobj: Match[str]) -> str:
i = ord(matchobj.group())
if i <= 0xFF:
return "#x%02X" % i
else:
return "#x%04X" % i

# The spec range of valid chars is:
# Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]
# For an unknown(?) reason, we disallow #x7F (DEL) as well.
illegal_xml_re = (
"[^\u0009\u000a\u000d\u0020-\u007e\u0080-\ud7ff\ue000-\ufffd\u10000-\u10ffff]"
)
return re.sub(illegal_xml_re, repl, str(arg))


def merge_family(left, right) -> None:
result = {}
for kl, vl in left.items():
for kr, vr in right.items():
if not isinstance(vl, list):
raise TypeError(type(vl))
result[kl] = vl + vr
left.update(result)


families = {}
families["_base"] = {"testcase": ["classname", "name"]}
families["_base_legacy"] = {"testcase": ["file", "line", "url"]}

# xUnit 1.x inherits legacy attributes.
families["xunit1"] = families["_base"].copy()
merge_family(families["xunit1"], families["_base_legacy"])

# xUnit 2.x uses strict base attributes.
families["xunit2"] = families["_base"]


class _NodeReporter:
def __init__(self, nodeid: Union[str, TestReport], xml: "LogXML") -> None:
self.id = nodeid
self.xml = xml
self.add_stats = self.xml.add_stats
self.family = self.xml.family
self.duration = 0.0
self.properties: List[Tuple[str, str]] = []
self.nodes: List[ET.Element] = []
self.attrs: Dict[str, str] = {}

def append(self, node: ET.Element) -> None:
self.xml.add_stats(node.tag)
self.nodes.append(node)

def add_property(self, name: str, value: object) -> None:
self.properties.append((str(name), bin_xml_escape(value)))

def add_attribute(self, name: str, value: object) -> None:
self.attrs[str(name)] = bin_xml_escape(value)

def make_properties_node(self) -> Optional[ET.Element]:
"""Return a Junit node containing custom properties, if any."""
if self.properties:
properties = ET.Element("properties")
for name, value in self.properties:
properties.append(ET.Element("property", name=name, value=value))
return properties
return None

def record_testreport(self, testreport: TestReport) -> None:
names = mangle_test_address(testreport.nodeid)
existing_attrs = self.attrs
classnames = names[:-1]
if self.xml.prefix:
classnames.insert(0, self.xml.prefix)
attrs: Dict[str, str] = {
"classname": ".".join(classnames),
"name": bin_xml_escape(names[-1]),
"file": testreport.location[0],
}
if testreport.location[1] is not None:
attrs["line"] = str(testreport.location[1])
if hasattr(testreport, "url"):
attrs["url"] = testreport.url
self.attrs = attrs
self.attrs.update(existing_attrs) # Restore any user-defined attributes.

# Preserve legacy testcase behavior.
if self.family == "xunit1":
return

# Filter out attributes not permitted by this test family.
# Including custom attributes because they are not valid here.
temp_attrs = {}
for key in self.attrs:
if key in families[self.family]["testcase"]:
temp_attrs[key] = self.attrs[key]
self.attrs = temp_attrs

def to_xml(self) -> ET.Element:
testcase = ET.Element("testcase", self.attrs, time="%.3f" % self.duration)
properties = self.make_properties_node()
if properties is not None:
testcase.append(properties)
testcase.extend(self.nodes)
return testcase

def _add_simple(self, tag: str, message: str, data: Optional[str] = None) -> None:
node = ET.Element(tag, message=message)
node.text = bin_xml_escape(data)
self.append(node)

def write_captured_output(self, report: TestReport) -> None:
if not self.xml.log_passing_tests and report.passed:
return

content_out = report.capstdout
content_log = report.caplog
content_err = report.capstderr
if self.xml.logging == "no":
return
content_all = ""
if self.xml.logging in ["log", "all"]:
content_all = self._prepare_content(content_log, " Captured Log ")
if self.xml.logging in ["system-out", "out-err", "all"]:
content_all += self._prepare_content(content_out, " Captured Out ")
self._write_content(report, content_all, "system-out")
content_all = ""
if self.xml.logging in ["system-err", "out-err", "all"]:
content_all += self._prepare_content(content_err, " Captured Err ")
self._write_content(report, content_all, "system-err")
content_all = ""
if content_all:
self._write_content(report, content_all, "system-out")

def _prepare_content(self, content: str, header: str) -> str:
return "\n".join([header.center(80, "-"), content, ""])

def _write_content(self, report: TestReport, content: str, jheader: str) -> None:
tag = ET.Element(jheader)
tag.text = bin_xml_escape(content)
self.append(tag)

def append_pass(self, report: TestReport) -> None:
self.add_stats("passed")

def append_failure(self, report: TestReport) -> None:
# msg = str(report.longrepr.reprtraceback.extraline)
if hasattr(report, "wasxfail"):
self._add_simple("skipped", "xfail-marked test passes unexpectedly")
else:
assert report.longrepr is not None
reprcrash: Optional[ReprFileLocation] = getattr(
report.longrepr, "reprcrash", None
)
if reprcrash is not None:
message = reprcrash.message
else:
message = str(report.longrepr)
message = bin_xml_escape(message)
self._add_simple("failure", message, str(report.longrepr))

def append_collect_error(self, report: TestReport) -> None:
# msg = str(report.longrepr.reprtraceback.extraline)
assert report.longrepr is not None
self._add_simple("error", "collection failure", str(report.longrepr))

def append_collect_skipped(self, report: TestReport) -> None:
self._add_simple("skipped", "collection skipped", str(report.longrepr))

def append_error(self, report: TestReport) -> None:
assert report.longrepr is not None
reprcrash: Optional[ReprFileLocation] = getattr(
report.longrepr, "reprcrash", None
)
if reprcrash is not None:
reason = reprcrash.message
else:
reason = str(report.longrepr)

if report.when == "teardown":
msg = f'failed on teardown with "{reason}"'
else:
msg = f'failed on setup with "{reason}"'
self._add_simple("error", bin_xml_escape(msg), str(report.longrepr))

def append_skipped(self, report: TestReport) -> None:
if hasattr(report, "wasxfail"):
xfailreason = report.wasxfail
if xfailreason.startswith("reason: "):
xfailreason = xfailreason[8:]
xfailreason = bin_xml_escape(xfailreason)
skipped = ET.Element("skipped", type="pytest.xfail", message=xfailreason)
self.append(skipped)
else:
assert isinstance(report.longrepr, tuple)
filename, lineno, skipreason = report.longrepr
if skipreason.startswith("Skipped: "):
skipreason = skipreason[9:]
details = f"{filename}:{lineno}: {skipreason}"

skipped = ET.Element(
"skipped", type="pytest.skip", message=bin_xml_escape(skipreason)
)
skipped.text = bin_xml_escape(details)
self.append(skipped)
self.write_captured_output(report)

def finalize(self) -> None:
data = self.to_xml()
self.__dict__.clear()
# Type ignored because mypy doesn't like overriding a method.
# Also the return value doesn't match...
self.to_xml = lambda: data # type: ignore[method-assign]


def _warn_incompatibility_with_xunit2(
request: FixtureRequest, fixture_name: str
) -> None:
"""Emit a PytestWarning about the given fixture being incompatible with newer xunit revisions."""
from _pytest.warning_types import PytestWarning

xml = request.config.stash.get(xml_key, None)
if xml is not None and xml.family not in ("xunit1", "legacy"):
request.node.warn(
PytestWarning(
f"{fixture_name} is incompatible with junit_family '{xml.family}' (use 'legacy' or 'xunit1')"
)
)


@pytest.fixture
def record_property(request: FixtureRequest) -> Callable[[str, object], None]:
"""Add extra properties to the calling test.

User properties become part of the test report and are available to the
configured reporters, like JUnit XML.

The fixture is callable with ``name, value``. The value is automatically
XML-encoded.

Example::

def test_function(record_property):
record_property("example_key", 1)
"""
_warn_incompatibility_with_xunit2(request, "record_property")

def append_property(name: str, value: object) -> None:
request.node.user_properties.append((name, value))

return append_property


@pytest.fixture
def record_xml_attribute(request: FixtureRequest) -> Callable[[str, object], None]:
"""Add extra xml attributes to the tag for the calling test.

The fixture is callable with ``name, value``. The value is
automatically XML-encoded.
"""
from _pytest.warning_types import PytestExperimentalApiWarning

request.node.warn(
PytestExperimentalApiWarning("record_xml_attribute is an experimental feature")
)

_warn_incompatibility_with_xunit2(request, "record_xml_attribute")

# Declare noop
def add_attr_noop(name: str, value: object) -> None:
pass

attr_func = add_attr_noop

xml = request.config.stash.get(xml_key, None)
if xml is not None:
node_reporter = xml.node_reporter(request.node.nodeid)
attr_func = node_reporter.add_attribute

return attr_func


def _check_record_param_type(param: str, v: str) -> None:
"""Used by record_testsuite_property to check that the given parameter name is of the proper
type."""
__tracebackhide__ = True
if not isinstance(v, str):
msg = "{param} parameter needs to be a string, but {g} given" # type: ignore[unreachable]
raise TypeError(msg.format(param=param, g=type(v).__name__))


@pytest.fixture(scope="session")
def record_testsuite_property(request: FixtureRequest) -> Callable[[str, object], None]:
"""Record a new ``<property>`` tag as child of the root ``<testsuite>``.

This is suitable to writing global information regarding the entire test
suite, and is compatible with ``xunit2`` JUnit family.

This is a ``session``-scoped fixture which is called with ``(name, value)``. Example:

.. code-block:: python

def test_foo(record_testsuite_property):
record_testsuite_property("ARCH", "PPC")
record_testsuite_property("STORAGE_TYPE", "CEPH")

:param name:
The property name.
:param value:
The property value. Will be converted to a string.

.. warning::

Currently this fixture **does not work** with the
`pytest-xdist <https://github.com/pytest-dev/pytest-xdist>`__ plugin. See
:issue:`7767` for details.
"""
__tracebackhide__ = True

def record_func(name: str, value: object) -> None:
"""No-op function in case --junit-xml was not passed in the command-line."""
__tracebackhide__ = True
_check_record_param_type("name", name)

xml = request.config.stash.get(xml_key, None)
if xml is not None:
record_func = xml.add_global_property
return record_func


def pytest_addoption(parser: Parser) -> None:
group = parser.getgroup("terminal reporting")
group.addoption(
"--junitxml",
"--junit-xml",
action="store",
dest="xmlpath",
metavar="path",
type=functools.partial(filename_arg, optname="--junitxml"),
default=None,
help="Create junit-xml style report file at given path",
)
group.addoption(
"--junitprefix",
"--junit-prefix",
action="store",
metavar="str",
default=None,
help="Prepend prefix to classnames in junit-xml output",
)
parser.addini(
"junit_suite_name", "Test suite name for JUnit report", default="pytest"
)
parser.addini(
"junit_logging",
"Write captured log messages to JUnit report: "
"one of no|log|system-out|system-err|out-err|all",
default="no",
)
parser.addini(
"junit_log_passing_tests",
"Capture log information for passing tests to JUnit report: ",
type="bool",
default=True,
)
parser.addini(
"junit_duration_report",
"Duration time to report: one of total|call",
default="total",
) # choices=['total', 'call'])
parser.addini(
"junit_family",
"Emit XML for schema: one of legacy|xunit1|xunit2",
default="xunit2",
)


def pytest_configure(config: Config) -> None:
xmlpath = config.option.xmlpath
# Prevent opening xmllog on worker nodes (xdist).
if xmlpath and not hasattr(config, "workerinput"):
junit_family = config.getini("junit_family")
config.stash[xml_key] = LogXML(
xmlpath,
config.option.junitprefix,
config.getini("junit_suite_name"),
config.getini("junit_logging"),
config.getini("junit_duration_report"),
junit_family,
config.getini("junit_log_passing_tests"),
)
config.pluginmanager.register(config.stash[xml_key])


def pytest_unconfigure(config: Config) -> None:
xml = config.stash.get(xml_key, None)
if xml:
del config.stash[xml_key]
config.pluginmanager.unregister(xml)


def mangle_test_address(address: str) -> List[str]:
path, possible_open_bracket, params = address.partition("[")
names = path.split("::")
# Convert file path to dotted path.
names[0] = names[0].replace(nodes.SEP, ".")
names[0] = re.sub(r"\.py$", "", names[0])
# Put any params back.
names[-1] += possible_open_bracket + params
return names


class LogXML:
def __init__(
self,
logfile,
prefix: Optional[str],
suite_name: str = "pytest",
logging: str = "no",
report_duration: str = "total",
family="xunit1",
log_passing_tests: bool = True,
) -> None:
logfile = os.path.expanduser(os.path.expandvars(logfile))
self.logfile = os.path.normpath(os.path.abspath(logfile))
self.prefix = prefix
self.suite_name = suite_name
self.logging = logging
self.log_passing_tests = log_passing_tests
self.report_duration = report_duration
self.family = family
self.stats: Dict[str, int] = dict.fromkeys(
["error", "passed", "failure", "skipped"], 0
)
self.node_reporters: Dict[
Tuple[Union[str, TestReport], object], _NodeReporter
] = {}
self.node_reporters_ordered: List[_NodeReporter] = []
self.global_properties: List[Tuple[str, str]] = []

# List of reports that failed on call but teardown is pending.
self.open_reports: List[TestReport] = []
self.cnt_double_fail_tests = 0

# Replaces convenience family with real family.
if self.family == "legacy":
self.family = "xunit1"

def finalize(self, report: TestReport) -> None:
nodeid = getattr(report, "nodeid", report)
# Local hack to handle xdist report order.
workernode = getattr(report, "node", None)
reporter = self.node_reporters.pop((nodeid, workernode))

for propname, propvalue in report.user_properties:
reporter.add_property(propname, str(propvalue))

if reporter is not None:
reporter.finalize()

def node_reporter(self, report: Union[TestReport, str]) -> _NodeReporter:
nodeid: Union[str, TestReport] = getattr(report, "nodeid", report)
# Local hack to handle xdist report order.
workernode = getattr(report, "node", None)

key = nodeid, workernode

if key in self.node_reporters:
# TODO: breaks for --dist=each
return self.node_reporters[key]

reporter = _NodeReporter(nodeid, self)

self.node_reporters[key] = reporter
self.node_reporters_ordered.append(reporter)

return reporter

def add_stats(self, key: str) -> None:
if key in self.stats:
self.stats[key] += 1

def _opentestcase(self, report: TestReport) -> _NodeReporter:
reporter = self.node_reporter(report)
reporter.record_testreport(report)
return reporter

def pytest_runtest_logreport(self, report: TestReport) -> None:
"""Handle a setup/call/teardown report, generating the appropriate
XML tags as necessary.

Note: due to plugins like xdist, this hook may be called in interlaced
order with reports from other nodes. For example:

Usual call order:
-> setup node1
-> call node1
-> teardown node1
-> setup node2
-> call node2
-> teardown node2

Possible call order in xdist:
-> setup node1
-> call node1
-> setup node2
-> call node2
-> teardown node2
-> teardown node1
"""
close_report = None
if report.passed:
if report.when == "call": # ignore setup/teardown
reporter = self._opentestcase(report)
reporter.append_pass(report)
elif report.failed:
if report.when == "teardown":
# The following vars are needed when xdist plugin is used.
report_wid = getattr(report, "worker_id", None)
report_ii = getattr(report, "item_index", None)
close_report = next(
(
rep
for rep in self.open_reports
if (
rep.nodeid == report.nodeid
and getattr(rep, "item_index", None) == report_ii
and getattr(rep, "worker_id", None) == report_wid
)
),
None,
)
if close_report:
# We need to open new testcase in case we have failure in
# call and error in teardown in order to follow junit
# schema.
self.finalize(close_report)
self.cnt_double_fail_tests += 1
reporter = self._opentestcase(report)
if report.when == "call":
reporter.append_failure(report)
self.open_reports.append(report)
if not self.log_passing_tests:
reporter.write_captured_output(report)
else:
reporter.append_error(report)
elif report.skipped:
reporter = self._opentestcase(report)
reporter.append_skipped(report)
self.update_testcase_duration(report)
if report.when == "teardown":
reporter = self._opentestcase(report)
reporter.write_captured_output(report)

self.finalize(report)
report_wid = getattr(report, "worker_id", None)
report_ii = getattr(report, "item_index", None)
close_report = next(
(
rep
for rep in self.open_reports
if (
rep.nodeid == report.nodeid
and getattr(rep, "item_index", None) == report_ii
and getattr(rep, "worker_id", None) == report_wid
)
),
None,
)
if close_report:
self.open_reports.remove(close_report)

def update_testcase_duration(self, report: TestReport) -> None:
"""Accumulate total duration for nodeid from given report and update
the Junit.testcase with the new total if already created."""
if self.report_duration in {"total", report.when}:
reporter = self.node_reporter(report)
reporter.duration += getattr(report, "duration", 0.0)

def pytest_collectreport(self, report: TestReport) -> None:
if not report.passed:
reporter = self._opentestcase(report)
if report.failed:
reporter.append_collect_error(report)
else:
reporter.append_collect_skipped(report)

def pytest_internalerror(self, excrepr: ExceptionRepr) -> None:
reporter = self.node_reporter("internal")
reporter.attrs.update(classname="pytest", name="internal")
reporter._add_simple("error", "internal error", str(excrepr))

def pytest_sessionstart(self) -> None:
self.suite_start_time = timing.time()

def pytest_sessionfinish(self) -> None:
dirname = os.path.dirname(os.path.abspath(self.logfile))
# exist_ok avoids filesystem race conditions between checking path existence and requesting creation
os.makedirs(dirname, exist_ok=True)

with open(self.logfile, "w", encoding="utf-8") as logfile:
suite_stop_time = timing.time()
suite_time_delta = suite_stop_time - self.suite_start_time

numtests = (
self.stats["passed"]
+ self.stats["failure"]
+ self.stats["skipped"]
+ self.stats["error"]
- self.cnt_double_fail_tests
)
logfile.write('<?xml version="1.0" encoding="utf-8"?>')

suite_node = ET.Element(
"testsuite",
name=self.suite_name,
errors=str(self.stats["error"]),
failures=str(self.stats["failure"]),
skipped=str(self.stats["skipped"]),
tests=str(numtests),
time="%.3f" % suite_time_delta,
timestamp=datetime.fromtimestamp(self.suite_start_time).isoformat(),
hostname=platform.node(),
)
global_properties = self._get_global_properties_node()
if global_properties is not None:
suite_node.append(global_properties)
for node_reporter in self.node_reporters_ordered:
suite_node.append(node_reporter.to_xml())
testsuites = ET.Element("testsuites")
testsuites.append(suite_node)
logfile.write(ET.tostring(testsuites, encoding="unicode"))

def pytest_terminal_summary(self, terminalreporter: TerminalReporter) -> None:
terminalreporter.write_sep("-", f"generated xml file: {self.logfile}")

def add_global_property(self, name: str, value: object) -> None:
__tracebackhide__ = True
_check_record_param_type("name", name)
self.global_properties.append((name, bin_xml_escape(value)))

def _get_global_properties_node(self) -> Optional[ET.Element]:
"""Return a Junit node containing custom properties, if any."""
if self.global_properties:
properties = ET.Element("properties")
for name, value in self.global_properties:
properties.append(ET.Element("property", name=name, value=value))
return properties
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Do not commit virtual environment files to version control.

This file is part of the pytest package installed in .venv/lib/python3.12/site-packages/. Virtual environment directories should never be committed to a repository because:

  1. They contain platform-specific binaries that won't work across different OS/architectures
  2. They massively bloat the repository (often 100+ MB)
  3. Dependencies should be installed via pip install -r requirements.txt or similar

Immediate actions required:

  1. Remove the .venv/ directory from the repository:

    git rm -r --cached .venv/
  2. Add .venv/ to your .gitignore:

    + .venv/
    + venv/
    + env/
    + __pycache__/
    + *.pyc
  3. Ensure dependencies are documented in requirements.txt or pyproject.toml

The PR objective mentions creating app/core/time_tracker.py - please ensure only your application code changes are committed, not the entire virtual environment.

🧰 Tools
🪛 Ruff (0.14.7)

72-72: Loop control variable kr not used within loop body

Rename unused kr to _kr

(B007)


190-190: Unused method argument: report

(ARG002)


195-195: Unused method argument: report

(ARG002)


372-372: Unused function argument: value

(ARG001)

🤖 Prompt for AI Agents
.venv/lib/python3.12/site-packages/_pytest/junitxml.py lines 1-700: the PR
accidentally included the project's virtual environment (`.venv`) containing
third-party packages; remove the venv files from the repo, stop tracking them
and ignore them going forward by running git rm -r --cached .venv/ (or remove
the folder and its files if appropriate), add `.venv/` to `.gitignore`, commit
that change, and then re-commit only your intended application files (e.g.
app/core/time_tracker.py); also ensure your dependencies are recorded in
requirements.txt or pyproject.toml so others can recreate the environment.

Comment on lines 1 to 479
# mypy: allow-untyped-defs
"""Add backward compatibility support for the legacy py path type."""

import dataclasses
from pathlib import Path
import shlex
import subprocess
from typing import Final
from typing import final
from typing import List
from typing import Optional
from typing import TYPE_CHECKING
from typing import Union

from iniconfig import SectionWrapper

from _pytest.cacheprovider import Cache
from _pytest.compat import LEGACY_PATH
from _pytest.compat import legacy_path
from _pytest.config import Config
from _pytest.config import hookimpl
from _pytest.config import PytestPluginManager
from _pytest.deprecated import check_ispytest
from _pytest.fixtures import fixture
from _pytest.fixtures import FixtureRequest
from _pytest.main import Session
from _pytest.monkeypatch import MonkeyPatch
from _pytest.nodes import Collector
from _pytest.nodes import Item
from _pytest.nodes import Node
from _pytest.pytester import HookRecorder
from _pytest.pytester import Pytester
from _pytest.pytester import RunResult
from _pytest.terminal import TerminalReporter
from _pytest.tmpdir import TempPathFactory


if TYPE_CHECKING:
import pexpect


@final
class Testdir:
"""
Similar to :class:`Pytester`, but this class works with legacy legacy_path objects instead.

All methods just forward to an internal :class:`Pytester` instance, converting results
to `legacy_path` objects as necessary.
"""

__test__ = False

CLOSE_STDIN: "Final" = Pytester.CLOSE_STDIN
TimeoutExpired: "Final" = Pytester.TimeoutExpired

def __init__(self, pytester: Pytester, *, _ispytest: bool = False) -> None:
check_ispytest(_ispytest)
self._pytester = pytester

@property
def tmpdir(self) -> LEGACY_PATH:
"""Temporary directory where tests are executed."""
return legacy_path(self._pytester.path)

@property
def test_tmproot(self) -> LEGACY_PATH:
return legacy_path(self._pytester._test_tmproot)

@property
def request(self):
return self._pytester._request

@property
def plugins(self):
return self._pytester.plugins

@plugins.setter
def plugins(self, plugins):
self._pytester.plugins = plugins

@property
def monkeypatch(self) -> MonkeyPatch:
return self._pytester._monkeypatch

def make_hook_recorder(self, pluginmanager) -> HookRecorder:
"""See :meth:`Pytester.make_hook_recorder`."""
return self._pytester.make_hook_recorder(pluginmanager)

def chdir(self) -> None:
"""See :meth:`Pytester.chdir`."""
return self._pytester.chdir()

def finalize(self) -> None:
return self._pytester._finalize()

def makefile(self, ext, *args, **kwargs) -> LEGACY_PATH:
"""See :meth:`Pytester.makefile`."""
if ext and not ext.startswith("."):
# pytester.makefile is going to throw a ValueError in a way that
# testdir.makefile did not, because
# pathlib.Path is stricter suffixes than py.path
# This ext arguments is likely user error, but since testdir has
# allowed this, we will prepend "." as a workaround to avoid breaking
# testdir usage that worked before
ext = "." + ext
return legacy_path(self._pytester.makefile(ext, *args, **kwargs))

def makeconftest(self, source) -> LEGACY_PATH:
"""See :meth:`Pytester.makeconftest`."""
return legacy_path(self._pytester.makeconftest(source))

def makeini(self, source) -> LEGACY_PATH:
"""See :meth:`Pytester.makeini`."""
return legacy_path(self._pytester.makeini(source))

def getinicfg(self, source: str) -> SectionWrapper:
"""See :meth:`Pytester.getinicfg`."""
return self._pytester.getinicfg(source)

def makepyprojecttoml(self, source) -> LEGACY_PATH:
"""See :meth:`Pytester.makepyprojecttoml`."""
return legacy_path(self._pytester.makepyprojecttoml(source))

def makepyfile(self, *args, **kwargs) -> LEGACY_PATH:
"""See :meth:`Pytester.makepyfile`."""
return legacy_path(self._pytester.makepyfile(*args, **kwargs))

def maketxtfile(self, *args, **kwargs) -> LEGACY_PATH:
"""See :meth:`Pytester.maketxtfile`."""
return legacy_path(self._pytester.maketxtfile(*args, **kwargs))

def syspathinsert(self, path=None) -> None:
"""See :meth:`Pytester.syspathinsert`."""
return self._pytester.syspathinsert(path)

def mkdir(self, name) -> LEGACY_PATH:
"""See :meth:`Pytester.mkdir`."""
return legacy_path(self._pytester.mkdir(name))

def mkpydir(self, name) -> LEGACY_PATH:
"""See :meth:`Pytester.mkpydir`."""
return legacy_path(self._pytester.mkpydir(name))

def copy_example(self, name=None) -> LEGACY_PATH:
"""See :meth:`Pytester.copy_example`."""
return legacy_path(self._pytester.copy_example(name))

def getnode(self, config: Config, arg) -> Optional[Union[Item, Collector]]:
"""See :meth:`Pytester.getnode`."""
return self._pytester.getnode(config, arg)

def getpathnode(self, path):
"""See :meth:`Pytester.getpathnode`."""
return self._pytester.getpathnode(path)

def genitems(self, colitems: List[Union[Item, Collector]]) -> List[Item]:
"""See :meth:`Pytester.genitems`."""
return self._pytester.genitems(colitems)

def runitem(self, source):
"""See :meth:`Pytester.runitem`."""
return self._pytester.runitem(source)

def inline_runsource(self, source, *cmdlineargs):
"""See :meth:`Pytester.inline_runsource`."""
return self._pytester.inline_runsource(source, *cmdlineargs)

def inline_genitems(self, *args):
"""See :meth:`Pytester.inline_genitems`."""
return self._pytester.inline_genitems(*args)

def inline_run(self, *args, plugins=(), no_reraise_ctrlc: bool = False):
"""See :meth:`Pytester.inline_run`."""
return self._pytester.inline_run(
*args, plugins=plugins, no_reraise_ctrlc=no_reraise_ctrlc
)

def runpytest_inprocess(self, *args, **kwargs) -> RunResult:
"""See :meth:`Pytester.runpytest_inprocess`."""
return self._pytester.runpytest_inprocess(*args, **kwargs)

def runpytest(self, *args, **kwargs) -> RunResult:
"""See :meth:`Pytester.runpytest`."""
return self._pytester.runpytest(*args, **kwargs)

def parseconfig(self, *args) -> Config:
"""See :meth:`Pytester.parseconfig`."""
return self._pytester.parseconfig(*args)

def parseconfigure(self, *args) -> Config:
"""See :meth:`Pytester.parseconfigure`."""
return self._pytester.parseconfigure(*args)

def getitem(self, source, funcname="test_func"):
"""See :meth:`Pytester.getitem`."""
return self._pytester.getitem(source, funcname)

def getitems(self, source):
"""See :meth:`Pytester.getitems`."""
return self._pytester.getitems(source)

def getmodulecol(self, source, configargs=(), withinit=False):
"""See :meth:`Pytester.getmodulecol`."""
return self._pytester.getmodulecol(
source, configargs=configargs, withinit=withinit
)

def collect_by_name(
self, modcol: Collector, name: str
) -> Optional[Union[Item, Collector]]:
"""See :meth:`Pytester.collect_by_name`."""
return self._pytester.collect_by_name(modcol, name)

def popen(
self,
cmdargs,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
stdin=CLOSE_STDIN,
**kw,
):
"""See :meth:`Pytester.popen`."""
return self._pytester.popen(cmdargs, stdout, stderr, stdin, **kw)

def run(self, *cmdargs, timeout=None, stdin=CLOSE_STDIN) -> RunResult:
"""See :meth:`Pytester.run`."""
return self._pytester.run(*cmdargs, timeout=timeout, stdin=stdin)

def runpython(self, script) -> RunResult:
"""See :meth:`Pytester.runpython`."""
return self._pytester.runpython(script)

def runpython_c(self, command):
"""See :meth:`Pytester.runpython_c`."""
return self._pytester.runpython_c(command)

def runpytest_subprocess(self, *args, timeout=None) -> RunResult:
"""See :meth:`Pytester.runpytest_subprocess`."""
return self._pytester.runpytest_subprocess(*args, timeout=timeout)

def spawn_pytest(
self, string: str, expect_timeout: float = 10.0
) -> "pexpect.spawn":
"""See :meth:`Pytester.spawn_pytest`."""
return self._pytester.spawn_pytest(string, expect_timeout=expect_timeout)

def spawn(self, cmd: str, expect_timeout: float = 10.0) -> "pexpect.spawn":
"""See :meth:`Pytester.spawn`."""
return self._pytester.spawn(cmd, expect_timeout=expect_timeout)

def __repr__(self) -> str:
return f"<Testdir {self.tmpdir!r}>"

def __str__(self) -> str:
return str(self.tmpdir)


class LegacyTestdirPlugin:
@staticmethod
@fixture
def testdir(pytester: Pytester) -> Testdir:
"""
Identical to :fixture:`pytester`, and provides an instance whose methods return
legacy ``LEGACY_PATH`` objects instead when applicable.

New code should avoid using :fixture:`testdir` in favor of :fixture:`pytester`.
"""
return Testdir(pytester, _ispytest=True)


@final
@dataclasses.dataclass
class TempdirFactory:
"""Backward compatibility wrapper that implements ``py.path.local``
for :class:`TempPathFactory`.

.. note::
These days, it is preferred to use ``tmp_path_factory``.

:ref:`About the tmpdir and tmpdir_factory fixtures<tmpdir and tmpdir_factory>`.

"""

_tmppath_factory: TempPathFactory

def __init__(
self, tmppath_factory: TempPathFactory, *, _ispytest: bool = False
) -> None:
check_ispytest(_ispytest)
self._tmppath_factory = tmppath_factory

def mktemp(self, basename: str, numbered: bool = True) -> LEGACY_PATH:
"""Same as :meth:`TempPathFactory.mktemp`, but returns a ``py.path.local`` object."""
return legacy_path(self._tmppath_factory.mktemp(basename, numbered).resolve())

def getbasetemp(self) -> LEGACY_PATH:
"""Same as :meth:`TempPathFactory.getbasetemp`, but returns a ``py.path.local`` object."""
return legacy_path(self._tmppath_factory.getbasetemp().resolve())


class LegacyTmpdirPlugin:
@staticmethod
@fixture(scope="session")
def tmpdir_factory(request: FixtureRequest) -> TempdirFactory:
"""Return a :class:`pytest.TempdirFactory` instance for the test session."""
# Set dynamically by pytest_configure().
return request.config._tmpdirhandler # type: ignore

@staticmethod
@fixture
def tmpdir(tmp_path: Path) -> LEGACY_PATH:
"""Return a temporary directory path object which is unique to each test
function invocation, created as a sub directory of the base temporary
directory.

By default, a new base temporary directory is created each test session,
and old bases are removed after 3 sessions, to aid in debugging. If
``--basetemp`` is used then it is cleared each session. See
:ref:`temporary directory location and retention`.

The returned object is a `legacy_path`_ object.

.. note::
These days, it is preferred to use ``tmp_path``.

:ref:`About the tmpdir and tmpdir_factory fixtures<tmpdir and tmpdir_factory>`.

.. _legacy_path: https://py.readthedocs.io/en/latest/path.html
"""
return legacy_path(tmp_path)


def Cache_makedir(self: Cache, name: str) -> LEGACY_PATH:
"""Return a directory path object with the given name.

Same as :func:`mkdir`, but returns a legacy py path instance.
"""
return legacy_path(self.mkdir(name))


def FixtureRequest_fspath(self: FixtureRequest) -> LEGACY_PATH:
"""(deprecated) The file system path of the test module which collected this test."""
return legacy_path(self.path)


def TerminalReporter_startdir(self: TerminalReporter) -> LEGACY_PATH:
"""The directory from which pytest was invoked.

Prefer to use ``startpath`` which is a :class:`pathlib.Path`.

:type: LEGACY_PATH
"""
return legacy_path(self.startpath)


def Config_invocation_dir(self: Config) -> LEGACY_PATH:
"""The directory from which pytest was invoked.

Prefer to use :attr:`invocation_params.dir <InvocationParams.dir>`,
which is a :class:`pathlib.Path`.

:type: LEGACY_PATH
"""
return legacy_path(str(self.invocation_params.dir))


def Config_rootdir(self: Config) -> LEGACY_PATH:
"""The path to the :ref:`rootdir <rootdir>`.

Prefer to use :attr:`rootpath`, which is a :class:`pathlib.Path`.

:type: LEGACY_PATH
"""
return legacy_path(str(self.rootpath))


def Config_inifile(self: Config) -> Optional[LEGACY_PATH]:
"""The path to the :ref:`configfile <configfiles>`.

Prefer to use :attr:`inipath`, which is a :class:`pathlib.Path`.

:type: Optional[LEGACY_PATH]
"""
return legacy_path(str(self.inipath)) if self.inipath else None


def Session_stardir(self: Session) -> LEGACY_PATH:
"""The path from which pytest was invoked.

Prefer to use ``startpath`` which is a :class:`pathlib.Path`.

:type: LEGACY_PATH
"""
return legacy_path(self.startpath)


def Config__getini_unknown_type(
self, name: str, type: str, value: Union[str, List[str]]
):
if type == "pathlist":
# TODO: This assert is probably not valid in all cases.
assert self.inipath is not None
dp = self.inipath.parent
input_values = shlex.split(value) if isinstance(value, str) else value
return [legacy_path(str(dp / x)) for x in input_values]
else:
raise ValueError(f"unknown configuration type: {type}", value)


def Node_fspath(self: Node) -> LEGACY_PATH:
"""(deprecated) returns a legacy_path copy of self.path"""
return legacy_path(self.path)


def Node_fspath_set(self: Node, value: LEGACY_PATH) -> None:
self.path = Path(value)


@hookimpl(tryfirst=True)
def pytest_load_initial_conftests(early_config: Config) -> None:
"""Monkeypatch legacy path attributes in several classes, as early as possible."""
mp = MonkeyPatch()
early_config.add_cleanup(mp.undo)

# Add Cache.makedir().
mp.setattr(Cache, "makedir", Cache_makedir, raising=False)

# Add FixtureRequest.fspath property.
mp.setattr(FixtureRequest, "fspath", property(FixtureRequest_fspath), raising=False)

# Add TerminalReporter.startdir property.
mp.setattr(
TerminalReporter, "startdir", property(TerminalReporter_startdir), raising=False
)

# Add Config.{invocation_dir,rootdir,inifile} properties.
mp.setattr(Config, "invocation_dir", property(Config_invocation_dir), raising=False)
mp.setattr(Config, "rootdir", property(Config_rootdir), raising=False)
mp.setattr(Config, "inifile", property(Config_inifile), raising=False)

# Add Session.startdir property.
mp.setattr(Session, "startdir", property(Session_stardir), raising=False)

# Add pathlist configuration type.
mp.setattr(Config, "_getini_unknown_type", Config__getini_unknown_type)

# Add Node.fspath property.
mp.setattr(Node, "fspath", property(Node_fspath, Node_fspath_set), raising=False)


@hookimpl
def pytest_configure(config: Config) -> None:
"""Installs the LegacyTmpdirPlugin if the ``tmpdir`` plugin is also installed."""
if config.pluginmanager.has_plugin("tmpdir"):
mp = MonkeyPatch()
config.add_cleanup(mp.undo)
# Create TmpdirFactory and attach it to the config object.
#
# This is to comply with existing plugins which expect the handler to be
# available at pytest_configure time, but ideally should be moved entirely
# to the tmpdir_factory session fixture.
try:
tmp_path_factory = config._tmp_path_factory # type: ignore[attr-defined]
except AttributeError:
# tmpdir plugin is blocked.
pass
else:
_tmpdirhandler = TempdirFactory(tmp_path_factory, _ispytest=True)
mp.setattr(config, "_tmpdirhandler", _tmpdirhandler, raising=False)

config.pluginmanager.register(LegacyTmpdirPlugin, "legacypath-tmpdir")


@hookimpl
def pytest_plugin_registered(plugin: object, manager: PytestPluginManager) -> None:
# pytester is not loaded by default and is commonly loaded from a conftest,
# so checking for it in `pytest_configure` is not enough.
is_pytester = plugin is manager.get_plugin("pytester")
if is_pytester and not manager.is_registered(LegacyTestdirPlugin):
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if .venv exists and if it's in .gitignore
echo "=== Checking if .venv directory exists ==="
if [ -d .venv ]; then
  echo ".venv/ directory exists"
  echo "First few files in .venv:"
  find .venv -type f -print 2>/dev/null | head -20
else
  echo ".venv/ directory does NOT exist in repo root"
fi

echo ""
echo "=== Checking .gitignore ==="
if [ -f .gitignore ]; then
  echo "Contents of .gitignore:"
  cat .gitignore
else
  echo ".gitignore not found"
fi

echo ""
echo "=== Checking git status for .venv ==="
git status .venv 2>&1 || echo "No .venv tracked in git or .venv doesn't exist"

echo ""
echo "=== Checking if file path exists in repo ==="
if [ -f ".venv/lib/python3.12/site-packages/_pytest/legacypath.py" ]; then
  echo "File exists at: .venv/lib/python3.12/site-packages/_pytest/legacypath.py"
else
  echo "File does NOT exist at: .venv/lib/python3.12/site-packages/_pytest/legacypath.py"
fi

echo ""
echo "=== Listing files in repository root ==="
ls -la | head -20

Repository: LumintelAnalytics/ChainReport-API

Length of output: 1667


🏁 Script executed:

#!/bin/bash
# Check if .venv is tracked in git
echo "=== Checking git tracking for .venv ==="
git ls-files .venv 2>/dev/null | head -10
if [ $? -eq 0 ]; then
  COUNT=$(git ls-files .venv 2>/dev/null | wc -l)
  echo "Total .venv files tracked in git: $COUNT"
else
  echo "Error checking git tracking"
fi

echo ""
echo "=== Checking git log for .venv commits ==="
git log --oneline --all -- .venv 2>/dev/null | head -5
if [ $? -ne 0 ] || [ -z "$(git log --oneline --all -- .venv 2>/dev/null)" ]; then
  echo "No commits found for .venv/ in git history"
fi

echo ""
echo "=== Checking what this PR review is about ==="
echo "Current git HEAD:"
git log --oneline -1

echo ""
echo "=== Checking if there are any staged changes ==="
git diff --cached --name-only 2>/dev/null
if [ -z "$(git diff --cached --name-only 2>/dev/null)" ]; then
  echo "No staged changes found"
fi

echo ""
echo "=== Checking recent git branches/references ==="
git branch -a | head -10
git log --oneline --all | head -10

Repository: LumintelAnalytics/ChainReport-API

Length of output: 1564


🏁 Script executed:

#!/bin/bash
# Check if .venv changes are part of the current PR (coderabbit_83 branch)
echo "=== Checking diff between main and coderabbit_83 ==="
git diff main..coderabbit_83 --name-only | grep -c "\.venv" || echo "No .venv files in this PR diff"

echo ""
echo "=== Files changed in this PR ==="
git diff main..coderabbit_83 --name-only | head -20

echo ""
echo "=== Is legacypath.py being modified in this PR? ==="
git diff main..coderabbit_83 -- ".venv/lib/python3.12/site-packages/_pytest/legacypath.py" 2>/dev/null | wc -l
if [ $? -eq 0 ] && [ $(git diff main..coderabbit_83 -- ".venv/lib/python3.12/site-packages/_pytest/legacypath.py" 2>/dev/null | wc -l) -eq 0 ]; then
  echo "File is NOT being modified in this PR"
fi

Repository: LumintelAnalytics/ChainReport-API

Length of output: 621


Remove 4,855+ virtual environment files from this PR before merging.

This PR adds the entire .venv/ directory (Python virtual environment) to version control, including thousands of third-party package files. This creates critical problems:

  • Repository bloat: 4,855+ unnecessary files bloating the repository
  • Portability: Platform/architecture-specific binaries break across different environments
  • Merge conflicts: Virtual environment changes cause frequent, meaningless conflicts
  • Best practice violation: Dependencies belong in requirements.txt or pyproject.toml, not in git

Required actions:

  1. Remove all .venv/ files from this PR before committing
  2. Create a .gitignore file (if missing) with .venv/ to prevent future commits
  3. If .venv/ is already tracked in the repository, it should be removed from git history:
git rm -r --cached .venv/
git commit -m "Remove virtual environment from version control"

Users can restore the virtual environment locally by running pip install -r requirements.txt.

🧰 Tools
🪛 Ruff (0.14.7)

398-398: Unused function argument: name

(ARG001)


407-407: Avoid specifying long messages outside the exception class

(TRY003)

🤖 Prompt for AI Agents
In .venv/lib/python3.12/site-packages/_pytest/legacypath.py lines 1-479 the file
is part of a committed virtual environment; remove the entire .venv/ directory
from this PR, add ".venv/" to .gitignore (or update existing .gitignore),
untrack any already committed files with git rm -r --cached .venv/ and commit
that change, and ensure dependencies are represented in requirements.txt or
pyproject.toml so contributors can recreate the venv instead of checking it in.

Comment on lines 1 to 530
SQLAlchemy-2.0.31.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
SQLAlchemy-2.0.31.dist-info/LICENSE,sha256=PA9Zq4h9BB3mpOUv_j6e212VIt6Qn66abNettue-MpM,1100
SQLAlchemy-2.0.31.dist-info/METADATA,sha256=Fv-yTpqMYGTC6PU1Jc8Ka1Evr74lqaBGNQCG7ALeO3g,9632
SQLAlchemy-2.0.31.dist-info/RECORD,,
SQLAlchemy-2.0.31.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
SQLAlchemy-2.0.31.dist-info/WHEEL,sha256=OvtvnbpcaxHa5TgXgfC48E6JA7zLr7svMspPP7Vk5o8,152
SQLAlchemy-2.0.31.dist-info/top_level.txt,sha256=rp-ZgB7D8G11ivXON5VGPjupT1voYmWqkciDt5Uaw_Q,11
sqlalchemy/__init__.py,sha256=sz89h0rk4HnopZTE-CXiZz3UlVY8JOD-IuYU_8VFqxU,13033
sqlalchemy/__pycache__/__init__.cpython-312.pyc,,
sqlalchemy/__pycache__/events.cpython-312.pyc,,
sqlalchemy/__pycache__/exc.cpython-312.pyc,,
sqlalchemy/__pycache__/inspection.cpython-312.pyc,,
sqlalchemy/__pycache__/log.cpython-312.pyc,,
sqlalchemy/__pycache__/schema.cpython-312.pyc,,
sqlalchemy/__pycache__/types.cpython-312.pyc,,
sqlalchemy/connectors/__init__.py,sha256=PzXPqZqi3BzEnrs1eW0DcsR4lyknAzhhN9rWcQ97hb4,476
sqlalchemy/connectors/__pycache__/__init__.cpython-312.pyc,,
sqlalchemy/connectors/__pycache__/aioodbc.cpython-312.pyc,,
sqlalchemy/connectors/__pycache__/asyncio.cpython-312.pyc,,
sqlalchemy/connectors/__pycache__/pyodbc.cpython-312.pyc,,
sqlalchemy/connectors/aioodbc.py,sha256=GSTiNMO9h0qjPxgqaxDwWZ8HvhWMFNVR6MJQnN1oc40,5288
sqlalchemy/connectors/asyncio.py,sha256=K3U5857TD0GmJXIhxgRljKnJhJNzYFhQ8UjyFX0Yyoo,5878
sqlalchemy/connectors/pyodbc.py,sha256=t7AjyxIOnaWg3CrlUEpBs4Y5l0HFdNt3P_cSSKhbi0Y,8501
sqlalchemy/cyextension/__init__.py,sha256=GzhhN8cjMnDTE0qerlUlpbrNmFPHQWCZ4Gk74OAxl04,244
sqlalchemy/cyextension/__pycache__/__init__.cpython-312.pyc,,
sqlalchemy/cyextension/collections.cpython-312-x86_64-linux-gnu.so,sha256=yiWEUI6nEQ_I7qykcmoNBCbxIS5efJvVf6Us9OvALGM,1932032
sqlalchemy/cyextension/collections.pyx,sha256=L7DZ3DGKpgw2MT2ZZRRxCnrcyE5pU1NAFowWgAzQPEc,12571
sqlalchemy/cyextension/immutabledict.cpython-312-x86_64-linux-gnu.so,sha256=s5-ve63GkOH0Ed84oUMd5zUKVBSUubBJGwNEjeKuKvM,805632
sqlalchemy/cyextension/immutabledict.pxd,sha256=3x3-rXG5eRQ7bBnktZ-OJ9-6ft8zToPmTDOd92iXpB0,291
sqlalchemy/cyextension/immutabledict.pyx,sha256=KfDTYbTfebstE8xuqAtuXsHNAK0_b5q_ymUiinUe_xs,3535
sqlalchemy/cyextension/processors.cpython-312-x86_64-linux-gnu.so,sha256=0RbEYS4_mSdRUtsVOCYy8zq6gnI7rJjqaJ8jQPQVLDY,530680
sqlalchemy/cyextension/processors.pyx,sha256=R1rHsGLEaGeBq5VeCydjClzYlivERIJ9B-XLOJlf2MQ,1792
sqlalchemy/cyextension/resultproxy.cpython-312-x86_64-linux-gnu.so,sha256=sX2NgKKif6tedRM7wlbAl38SbQ771GMLCwPVXxyLQ50,621280
sqlalchemy/cyextension/resultproxy.pyx,sha256=eWLdyBXiBy_CLQrF5ScfWJm7X0NeelscSXedtj1zv9Q,2725
sqlalchemy/cyextension/util.cpython-312-x86_64-linux-gnu.so,sha256=Ags-M2QyDKaaMNiGZ2rKGxNa5O6G8F0ByiPKTLqjOhI,951048
sqlalchemy/cyextension/util.pyx,sha256=B85orxa9LddLuQEaDoVSq1XmAXIbLKxrxpvuB8ogV_o,2530
sqlalchemy/dialects/__init__.py,sha256=Kos9Gf5JZg1Vg6GWaCqEbD6e0r1jCwCmcnJIfcxDdcY,1770
sqlalchemy/dialects/__pycache__/__init__.cpython-312.pyc,,
sqlalchemy/dialects/__pycache__/_typing.cpython-312.pyc,,
sqlalchemy/dialects/_typing.py,sha256=hyv0nKucX2gI8ispB1IsvaUgrEPn9zEcq9hS7kfstEw,888
sqlalchemy/dialects/mssql/__init__.py,sha256=r5t8wFRNtBQoiUWh0WfIEWzXZW6f3D0uDt6NZTW_7Cc,1880
sqlalchemy/dialects/mssql/__pycache__/__init__.cpython-312.pyc,,
sqlalchemy/dialects/mssql/__pycache__/aioodbc.cpython-312.pyc,,
sqlalchemy/dialects/mssql/__pycache__/base.cpython-312.pyc,,
sqlalchemy/dialects/mssql/__pycache__/information_schema.cpython-312.pyc,,
sqlalchemy/dialects/mssql/__pycache__/json.cpython-312.pyc,,
sqlalchemy/dialects/mssql/__pycache__/provision.cpython-312.pyc,,
sqlalchemy/dialects/mssql/__pycache__/pymssql.cpython-312.pyc,,
sqlalchemy/dialects/mssql/__pycache__/pyodbc.cpython-312.pyc,,
sqlalchemy/dialects/mssql/aioodbc.py,sha256=UQd9ecSMIML713TDnLAviuBVJle7P7i1FtqGZZePk2Y,2022
sqlalchemy/dialects/mssql/base.py,sha256=87Z8bQbA8q5_T2Oyd4tUuttWNjYTQD9Dhq-eplDJcY4,132301
sqlalchemy/dialects/mssql/information_schema.py,sha256=HswjDc6y0mPXCf_x6VyylHlBdBa4PSY6Evxmmlch700,8084
sqlalchemy/dialects/mssql/json.py,sha256=evUACW2O62TAPq8B7QIPagz7jfc664ql9ms68JqiYzg,4816
sqlalchemy/dialects/mssql/provision.py,sha256=RTVbgYLFAHzEnpVQDJroU8ji_10MqBTiZfyP9_-QNT4,5362
sqlalchemy/dialects/mssql/pymssql.py,sha256=eZRLz7HGt3SdoZUjFBmA9BS43N7AhIASw7VPBPEJuG0,4038
sqlalchemy/dialects/mssql/pyodbc.py,sha256=vwM-vBlmRwrqxOc73P0sFOrBSwn24wzc5IkEOpalbXQ,27056
sqlalchemy/dialects/mysql/__init__.py,sha256=bxbi4hkysUK2OOVvr1F49akUj1cky27kKb07tgFzI9U,2153
sqlalchemy/dialects/mysql/__pycache__/__init__.cpython-312.pyc,,
sqlalchemy/dialects/mysql/__pycache__/aiomysql.cpython-312.pyc,,
sqlalchemy/dialects/mysql/__pycache__/asyncmy.cpython-312.pyc,,
sqlalchemy/dialects/mysql/__pycache__/base.cpython-312.pyc,,
sqlalchemy/dialects/mysql/__pycache__/cymysql.cpython-312.pyc,,
sqlalchemy/dialects/mysql/__pycache__/dml.cpython-312.pyc,,
sqlalchemy/dialects/mysql/__pycache__/enumerated.cpython-312.pyc,,
sqlalchemy/dialects/mysql/__pycache__/expression.cpython-312.pyc,,
sqlalchemy/dialects/mysql/__pycache__/json.cpython-312.pyc,,
sqlalchemy/dialects/mysql/__pycache__/mariadb.cpython-312.pyc,,
sqlalchemy/dialects/mysql/__pycache__/mariadbconnector.cpython-312.pyc,,
sqlalchemy/dialects/mysql/__pycache__/mysqlconnector.cpython-312.pyc,,
sqlalchemy/dialects/mysql/__pycache__/mysqldb.cpython-312.pyc,,
sqlalchemy/dialects/mysql/__pycache__/provision.cpython-312.pyc,,
sqlalchemy/dialects/mysql/__pycache__/pymysql.cpython-312.pyc,,
sqlalchemy/dialects/mysql/__pycache__/pyodbc.cpython-312.pyc,,
sqlalchemy/dialects/mysql/__pycache__/reflection.cpython-312.pyc,,
sqlalchemy/dialects/mysql/__pycache__/reserved_words.cpython-312.pyc,,
sqlalchemy/dialects/mysql/__pycache__/types.cpython-312.pyc,,
sqlalchemy/dialects/mysql/aiomysql.py,sha256=-oMZnCqNsSki8mlQRTWIwiQPT1OVdZIuANkb90q8LAs,9999
sqlalchemy/dialects/mysql/asyncmy.py,sha256=YpuuOh8VknEeqHqUXQGfQ3jhfO3Xb-vZv78Jq5cscJ0,10067
sqlalchemy/dialects/mysql/base.py,sha256=KA7tvRxKUw0KwHwMth2rz-NWV0xMkVbYvPoBM9wrAFw,120850
sqlalchemy/dialects/mysql/cymysql.py,sha256=eXT1ry0w_qRxjiO24M980c-8PZ9qSsbhqBHntjEiKB0,2300
sqlalchemy/dialects/mysql/dml.py,sha256=HXJMAvimJsqvhj3UZO4vW_6LkF5RqaKbHvklAjor7yU,7645
sqlalchemy/dialects/mysql/enumerated.py,sha256=ipEPPQqoXfFwcywNdcLlZCEzHBtnitHRah1Gn6nItcg,8448
sqlalchemy/dialects/mysql/expression.py,sha256=lsmQCHKwfPezUnt27d2kR6ohk4IRFCA64KBS16kx5dc,4097
sqlalchemy/dialects/mysql/json.py,sha256=l6MEZ0qp8FgiRrIQvOMhyEJq0q6OqiEnvDTx5Cbt9uQ,2269
sqlalchemy/dialects/mysql/mariadb.py,sha256=kTfBLioLKk4JFFst4TY_iWqPtnvvQXFHknLfm89H2N8,853
sqlalchemy/dialects/mysql/mariadbconnector.py,sha256=sRlzRFU5E1512aFyEhmmL1ffeU1u5meaLqtRYTcrH7Y,8568
sqlalchemy/dialects/mysql/mysqlconnector.py,sha256=qiQdfLPze3QHuASAZ9iqRzD0hDW8FbKoQnfAEQCF7tM,5675
sqlalchemy/dialects/mysql/mysqldb.py,sha256=zPHKMQbHu8bFuD7YvOJewn2axQbfwA93NZoeX3bbbiQ,9502
sqlalchemy/dialects/mysql/provision.py,sha256=4oGkClQ8jC3YLPF54sB4kCjFc8HRTwf5zl5zftAAXGo,3474
sqlalchemy/dialects/mysql/pymysql.py,sha256=GUnSHd2M2uKjmN46Hheymtm26g7phEgwYOXrX0zLY8M,4083
sqlalchemy/dialects/mysql/pyodbc.py,sha256=072crI4qVyPhajYvHnsfFeSrNjLFVPIjBQKo5uyz5yk,4297
sqlalchemy/dialects/mysql/reflection.py,sha256=3u34YwT1JJh3uThGZJZ3FKdnUcT7v08QB-tAl1r7VRk,22834
sqlalchemy/dialects/mysql/reserved_words.py,sha256=ucKX2p2c3UnMq2ayZuOHuf73eXhu7SKsOsTlIN1Q83I,9258
sqlalchemy/dialects/mysql/types.py,sha256=L5cTCsMT1pTedszNEM3jSxFNZEMcHQLprYCZ0vmfsnA,24343
sqlalchemy/dialects/oracle/__init__.py,sha256=p4-2gw7TT0bX_MoJXTGD4i8WHctYsK9kCRbkpzykBrc,1493
sqlalchemy/dialects/oracle/__pycache__/__init__.cpython-312.pyc,,
sqlalchemy/dialects/oracle/__pycache__/base.cpython-312.pyc,,
sqlalchemy/dialects/oracle/__pycache__/cx_oracle.cpython-312.pyc,,
sqlalchemy/dialects/oracle/__pycache__/dictionary.cpython-312.pyc,,
sqlalchemy/dialects/oracle/__pycache__/oracledb.cpython-312.pyc,,
sqlalchemy/dialects/oracle/__pycache__/provision.cpython-312.pyc,,
sqlalchemy/dialects/oracle/__pycache__/types.cpython-312.pyc,,
sqlalchemy/dialects/oracle/base.py,sha256=-7b5iubFPxJyDRoLXlxj8rk8eBRN2_IdZlB2zzzrrbw,118246
sqlalchemy/dialects/oracle/cx_oracle.py,sha256=t5yH4svVz7xoDSITF958blgZ01hbCUEWUKrAXwiCiAE,55566
sqlalchemy/dialects/oracle/dictionary.py,sha256=7WMrbPkqo8ZdGjaEZyQr-5f2pajSOF1OTGb8P97z8-g,19519
sqlalchemy/dialects/oracle/oracledb.py,sha256=UFcZwrrk0pWfAp_SKJZ1B5rIQHtNhOvuu73_JaSnTbI,9487
sqlalchemy/dialects/oracle/provision.py,sha256=O9ZpF4OG6Cx4mMzLRfZwhs8dZjrJETWR402n9c7726A,8304
sqlalchemy/dialects/oracle/types.py,sha256=QK3hJvWzKnnCe3oD3rItwEEIwcoBze8qGg7VFOvVlIk,8231
sqlalchemy/dialects/postgresql/__init__.py,sha256=wwnNAq4wDQzrlPRzDNB06ayuq3L2HNO99nzeEvq-YcU,3892
sqlalchemy/dialects/postgresql/__pycache__/__init__.cpython-312.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/_psycopg_common.cpython-312.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/array.cpython-312.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/asyncpg.cpython-312.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/base.cpython-312.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/dml.cpython-312.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/ext.cpython-312.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/hstore.cpython-312.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/json.cpython-312.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/named_types.cpython-312.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/operators.cpython-312.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/pg8000.cpython-312.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/pg_catalog.cpython-312.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/provision.cpython-312.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/psycopg.cpython-312.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/psycopg2.cpython-312.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/psycopg2cffi.cpython-312.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/ranges.cpython-312.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/types.cpython-312.pyc,,
sqlalchemy/dialects/postgresql/_psycopg_common.py,sha256=7TudtgsPiSB8O5kX8W8KxcNYR8t5h_UHb86b_ChL0P8,5696
sqlalchemy/dialects/postgresql/array.py,sha256=bWcame7ntmI_Kx6gmBX0-chwADFdLHeCvaDQ4iX8id8,13734
sqlalchemy/dialects/postgresql/asyncpg.py,sha256=nfujNbmAjkVPPt9jhh5ZJp8QziZQJ0fMG4JNnLTdsag,40206
sqlalchemy/dialects/postgresql/base.py,sha256=ed49Ode09deJF3jXr8V5sSHTwVoFVq0dlvNdP3mJaBQ,178989
sqlalchemy/dialects/postgresql/dml.py,sha256=Pc69Le6qzmUHHb1FT5zeUSD31dWm6SBgdCAGW89cs3s,11212
sqlalchemy/dialects/postgresql/ext.py,sha256=1bZ--iNh2O9ym7l2gXZX48yP3yMO4dqb9RpYro2Mj2Q,16262
sqlalchemy/dialects/postgresql/hstore.py,sha256=otAx-RTDfpi_tcXkMuQV0JOIXtYgevgnsikLKKOkI6U,11541
sqlalchemy/dialects/postgresql/json.py,sha256=9-ZvkLVMfQNSz0aHGml_B8sNg3gVq3-gxLnb5BO7Pog,11217
sqlalchemy/dialects/postgresql/named_types.py,sha256=3IV1ufo7zJjKmX4VtGDEnoXE6xEqLJAtGG82IiqHXwY,17594
sqlalchemy/dialects/postgresql/operators.py,sha256=NsAaWun_tL3d_be0fs9YL6T4LPKK6crnmFxxIJHgyeY,2808
sqlalchemy/dialects/postgresql/pg8000.py,sha256=3yoekiWSF-xnaWMqG76XrYPMqerg-42TdmfsW_ivK9E,18640
sqlalchemy/dialects/postgresql/pg_catalog.py,sha256=hY3NXEUHxTWD4umhd2aowNu3laC-61Q_qQ_pReyXTUM,9254
sqlalchemy/dialects/postgresql/provision.py,sha256=yqyx-aDFO9l2YcL9f4T5HBP_Lnt5dHsMjpuXUG8mi7A,5762
sqlalchemy/dialects/postgresql/psycopg.py,sha256=ACkfuT87vUJEW4kJyUqDYTwlnl5u0FZFlDcvLzxNzFQ,23226
sqlalchemy/dialects/postgresql/psycopg2.py,sha256=DbCB1-90O8YQDaIR67RtKOjzW3nvrdHoiwnVWYv_mX4,31605
sqlalchemy/dialects/postgresql/psycopg2cffi.py,sha256=M7wAYSL6Pvt-4nbfacAHGyyw4XMKJ_bQZ1tc1pBtIdg,1756
sqlalchemy/dialects/postgresql/ranges.py,sha256=6CgV7qkxEMJ9AQsiibo_XBLJYzGh-2ZxpG83sRaesVY,32949
sqlalchemy/dialects/postgresql/types.py,sha256=Jfxqw9JaKNOq29JRWBublywgb3lLMyzx8YZI7CXpS2s,7300
sqlalchemy/dialects/sqlite/__init__.py,sha256=lp9DIggNn349M-7IYhUA8et8--e8FRExWD2V_r1LJk4,1182
sqlalchemy/dialects/sqlite/__pycache__/__init__.cpython-312.pyc,,
sqlalchemy/dialects/sqlite/__pycache__/aiosqlite.cpython-312.pyc,,
sqlalchemy/dialects/sqlite/__pycache__/base.cpython-312.pyc,,
sqlalchemy/dialects/sqlite/__pycache__/dml.cpython-312.pyc,,
sqlalchemy/dialects/sqlite/__pycache__/json.cpython-312.pyc,,
sqlalchemy/dialects/sqlite/__pycache__/provision.cpython-312.pyc,,
sqlalchemy/dialects/sqlite/__pycache__/pysqlcipher.cpython-312.pyc,,
sqlalchemy/dialects/sqlite/__pycache__/pysqlite.cpython-312.pyc,,
sqlalchemy/dialects/sqlite/aiosqlite.py,sha256=g3qGV6jmiXabWyb3282g_Nmxtj1jThxGSe9C9yalb-U,12345
sqlalchemy/dialects/sqlite/base.py,sha256=lUtigjn7NdPBq831zQsLcBwdwRJqdgKM_tUaDrMElOE,96794
sqlalchemy/dialects/sqlite/dml.py,sha256=9GE55WvwoktKy2fHeT-Wbc9xPHgsbh5oBfd_fckMH5Q,8443
sqlalchemy/dialects/sqlite/json.py,sha256=Eoplbb_4dYlfrtmQaI8Xddd2suAIHA-IdbDQYM-LIhs,2777
sqlalchemy/dialects/sqlite/provision.py,sha256=UCpmwxf4IWlrpb2eLHGbPTpCFVbdI_KAh2mKtjiLYao,5632
sqlalchemy/dialects/sqlite/pysqlcipher.py,sha256=OL2S_05DK9kllZj6DOz7QtEl7jI7syxjW6woS725ii4,5356
sqlalchemy/dialects/sqlite/pysqlite.py,sha256=USPhTjA19ks7VfzjlMXN_BkUpZiPH8J5ZyR4B9DBhVY,28045
sqlalchemy/dialects/type_migration_guidelines.txt,sha256=-uHNdmYFGB7bzUNT6i8M5nb4j6j9YUKAtW4lcBZqsMg,8239
sqlalchemy/engine/__init__.py,sha256=Stb2oV6l8w65JvqEo6J4qtKoApcmOpXy3AAxQud4C1o,2818
sqlalchemy/engine/__pycache__/__init__.cpython-312.pyc,,
sqlalchemy/engine/__pycache__/_py_processors.cpython-312.pyc,,
sqlalchemy/engine/__pycache__/_py_row.cpython-312.pyc,,
sqlalchemy/engine/__pycache__/_py_util.cpython-312.pyc,,
sqlalchemy/engine/__pycache__/base.cpython-312.pyc,,
sqlalchemy/engine/__pycache__/characteristics.cpython-312.pyc,,
sqlalchemy/engine/__pycache__/create.cpython-312.pyc,,
sqlalchemy/engine/__pycache__/cursor.cpython-312.pyc,,
sqlalchemy/engine/__pycache__/default.cpython-312.pyc,,
sqlalchemy/engine/__pycache__/events.cpython-312.pyc,,
sqlalchemy/engine/__pycache__/interfaces.cpython-312.pyc,,
sqlalchemy/engine/__pycache__/mock.cpython-312.pyc,,
sqlalchemy/engine/__pycache__/processors.cpython-312.pyc,,
sqlalchemy/engine/__pycache__/reflection.cpython-312.pyc,,
sqlalchemy/engine/__pycache__/result.cpython-312.pyc,,
sqlalchemy/engine/__pycache__/row.cpython-312.pyc,,
sqlalchemy/engine/__pycache__/strategies.cpython-312.pyc,,
sqlalchemy/engine/__pycache__/url.cpython-312.pyc,,
sqlalchemy/engine/__pycache__/util.cpython-312.pyc,,
sqlalchemy/engine/_py_processors.py,sha256=j9i_lcYYQOYJMcsDerPxI0sVFBIlX5sqoYMdMJlgWPI,3744
sqlalchemy/engine/_py_row.py,sha256=wSqoUFzLOJ1f89kgDb6sJm9LUrF5LMFpXPcK1vUsKcs,3787
sqlalchemy/engine/_py_util.py,sha256=f2DI3AN1kv6EplelowesCVpwS8hSXNufRkZoQmJtSH8,2484
sqlalchemy/engine/base.py,sha256=0jDuJYb1tipD-N9chnMCvd25JySJQCm8CDCt_lobmv0,122898
sqlalchemy/engine/characteristics.py,sha256=N3kbvw_ApMh86wb5yAGnxtPYD4YRhYMWion1H_aVZBI,4765
sqlalchemy/engine/create.py,sha256=mYJtOG2ZKM8sgyfjpGpamW15RDU7JXi5s6iibbJHMIs,33206
sqlalchemy/engine/cursor.py,sha256=cFq61yrw76k-QR_xNUBWuL-Zeyb14ltG-6jo2Q2iuuw,76392
sqlalchemy/engine/default.py,sha256=B3i9Ce3Jk64CtNaS1gnes4uCMhiQn2cMwnZh5RxR-aE,84137
sqlalchemy/engine/events.py,sha256=c0unNFFiHzTAvkUtXoJaxzMFMDwurBkHiiUhuN8qluc,37381
sqlalchemy/engine/interfaces.py,sha256=n6G7VW5kaGyBlKdCkgaDs694tfzLPOxZeboDmVaJThg,112832
sqlalchemy/engine/mock.py,sha256=yvpxgFmRw5G4QsHeF-ZwQGHKES-HqQOucTxFtN1uzdk,4179
sqlalchemy/engine/processors.py,sha256=XyfINKbo-2fjN-mW55YybvFyQMOil50_kVqsunahkNs,2379
sqlalchemy/engine/reflection.py,sha256=FlT5kPpKm7Lah50GNt5XcnlJWojTL3LD_x0SoCF9kfY,75127
sqlalchemy/engine/result.py,sha256=j6BI4Wj2bziQNQG5OlG_Cm4KcNWY9AoYvTXVlJUU-D8,77603
sqlalchemy/engine/row.py,sha256=9AAQo9zYDL88GcZ3bjcQTwMT-YIcuGTSMAyTfmBJ_yM,12032
sqlalchemy/engine/strategies.py,sha256=DqFSWaXJPL-29Omot9O0aOcuGL8KmCGyOvnPGDkAJoE,442
sqlalchemy/engine/url.py,sha256=8eWkUaIUyDExOcJ2D4xJXRcn4OY1GQJ3Q2duSX6UGAg,30784
sqlalchemy/engine/util.py,sha256=bNirO8k1S8yOW61uNH-a9QrWtAJ9VGFgbiR0lk1lUQU,5682
sqlalchemy/event/__init__.py,sha256=KBrp622xojnC3FFquxa2JsMamwAbfkvzfv6Op0NKiYc,997
sqlalchemy/event/__pycache__/__init__.cpython-312.pyc,,
sqlalchemy/event/__pycache__/api.cpython-312.pyc,,
sqlalchemy/event/__pycache__/attr.cpython-312.pyc,,
sqlalchemy/event/__pycache__/base.cpython-312.pyc,,
sqlalchemy/event/__pycache__/legacy.cpython-312.pyc,,
sqlalchemy/event/__pycache__/registry.cpython-312.pyc,,
sqlalchemy/event/api.py,sha256=DtDVgjKSorOfp9MGJ7fgMWrj4seC_hkwF4D8CW1RFZU,8226
sqlalchemy/event/attr.py,sha256=X8QeHGK4ioSYht1vkhc11f606_mq_t91jMNIT314ubs,20751
sqlalchemy/event/base.py,sha256=XeNX-y2X2rYZqxNEnylx-_UUTYpVc4TWjLWIGjx4Jkc,15161
sqlalchemy/event/legacy.py,sha256=teMPs00fO-4g8a_z2omcVKkYce5wj_1uvJO2n2MIeuo,8227
sqlalchemy/event/registry.py,sha256=nfTSSyhjZZXc5wseWB4sXn-YibSc0LKX8mg17XlWmAo,10835
sqlalchemy/events.py,sha256=k-ZD38aSPD29LYhED7CBqttp5MDVVx_YSaWC2-cu9ec,525
sqlalchemy/exc.py,sha256=M_8-O1hd8i6gbyx-TapV400p_Lxq2QqTGMXUAO-YgCc,23976
sqlalchemy/ext/__init__.py,sha256=S1fGKAbycnQDV01gs-JWGaFQ9GCD4QHwKcU2wnugg_o,322
sqlalchemy/ext/__pycache__/__init__.cpython-312.pyc,,
sqlalchemy/ext/__pycache__/associationproxy.cpython-312.pyc,,
sqlalchemy/ext/__pycache__/automap.cpython-312.pyc,,
sqlalchemy/ext/__pycache__/baked.cpython-312.pyc,,
sqlalchemy/ext/__pycache__/compiler.cpython-312.pyc,,
sqlalchemy/ext/__pycache__/horizontal_shard.cpython-312.pyc,,
sqlalchemy/ext/__pycache__/hybrid.cpython-312.pyc,,
sqlalchemy/ext/__pycache__/indexable.cpython-312.pyc,,
sqlalchemy/ext/__pycache__/instrumentation.cpython-312.pyc,,
sqlalchemy/ext/__pycache__/mutable.cpython-312.pyc,,
sqlalchemy/ext/__pycache__/orderinglist.cpython-312.pyc,,
sqlalchemy/ext/__pycache__/serializer.cpython-312.pyc,,
sqlalchemy/ext/associationproxy.py,sha256=5O5ANHARO8jytvqBQmOu-QjNVE4Hh3tfYquqKAj5ajs,65771
sqlalchemy/ext/asyncio/__init__.py,sha256=1OqSxEyIUn7RWLGyO12F-jAUIvk1I6DXlVy80-Gvkds,1317
sqlalchemy/ext/asyncio/__pycache__/__init__.cpython-312.pyc,,
sqlalchemy/ext/asyncio/__pycache__/base.cpython-312.pyc,,
sqlalchemy/ext/asyncio/__pycache__/engine.cpython-312.pyc,,
sqlalchemy/ext/asyncio/__pycache__/exc.cpython-312.pyc,,
sqlalchemy/ext/asyncio/__pycache__/result.cpython-312.pyc,,
sqlalchemy/ext/asyncio/__pycache__/scoping.cpython-312.pyc,,
sqlalchemy/ext/asyncio/__pycache__/session.cpython-312.pyc,,
sqlalchemy/ext/asyncio/base.py,sha256=fl7wxZD9KjgFiCtG3WXrYjHEvanamcsodCqq9pH9lOk,8905
sqlalchemy/ext/asyncio/engine.py,sha256=S_IRWX4QAjj2veLSu4Y3gKBIXkKQt7_2StJAK2_KUDY,48190
sqlalchemy/ext/asyncio/exc.py,sha256=8sII7VMXzs2TrhizhFQMzSfcroRtiesq8o3UwLfXSgQ,639
sqlalchemy/ext/asyncio/result.py,sha256=ID2eh-NHW-lnNFTxbKhje8fr-tnsucUsiw_jcpGcSPc,30409
sqlalchemy/ext/asyncio/scoping.py,sha256=UxHAFxtWKqA7TEozyN2h7MJyzSspTCrS-1SlgQLTExo,52608
sqlalchemy/ext/asyncio/session.py,sha256=mkFFC1C2mPuopz3BwkfSVTlp3vuIDc8hxiQx5ky5rvc,63103
sqlalchemy/ext/automap.py,sha256=cMs3mMXZBbfQXahxHbT3-6h5uMM--GZIcdtwhRrZoio,61589
sqlalchemy/ext/baked.py,sha256=H6T1il7GY84BhzPFj49UECSpZh_eBuiHomA-QIsYOYQ,17807
sqlalchemy/ext/compiler.py,sha256=ONPoxoKD2yUS9R2-oOhmPsA7efm-Bs0BXo7HE1dGlsU,20391
sqlalchemy/ext/declarative/__init__.py,sha256=20psLdFQbbOWfpdXHZ0CTY6I1k4UqXvKemNVu1LvPOI,1818
sqlalchemy/ext/declarative/__pycache__/__init__.cpython-312.pyc,,
sqlalchemy/ext/declarative/__pycache__/extensions.cpython-312.pyc,,
sqlalchemy/ext/declarative/extensions.py,sha256=uCjN1GisQt54AjqYnKYzJdUjnGd2pZBW47WWdPlS7FE,19547
sqlalchemy/ext/horizontal_shard.py,sha256=wuwAPnHymln0unSBnyx-cpX0AfESKSsypaSQTYCvzDk,16750
sqlalchemy/ext/hybrid.py,sha256=IYkCaPZ29gm2cPKPg0cWMkLCEqMykD8-JJTvgacGbmc,52458
sqlalchemy/ext/indexable.py,sha256=UkTelbydKCdKelzbv3HWFFavoET9WocKaGRPGEOVfN8,11032
sqlalchemy/ext/instrumentation.py,sha256=sg8ghDjdHSODFXh_jAmpgemnNX1rxCeeXEG3-PMdrNk,15707
sqlalchemy/ext/mutable.py,sha256=L5ZkHBGYhMaqO75Xtyrk2DBR44RDk0g6Rz2HzHH0F8Q,37355
sqlalchemy/ext/mypy/__init__.py,sha256=0WebDIZmqBD0OTq5JLtd_PmfF9JGxe4d4Qv3Ml3PKUg,241
sqlalchemy/ext/mypy/__pycache__/__init__.cpython-312.pyc,,
sqlalchemy/ext/mypy/__pycache__/apply.cpython-312.pyc,,
sqlalchemy/ext/mypy/__pycache__/decl_class.cpython-312.pyc,,
sqlalchemy/ext/mypy/__pycache__/infer.cpython-312.pyc,,
sqlalchemy/ext/mypy/__pycache__/names.cpython-312.pyc,,
sqlalchemy/ext/mypy/__pycache__/plugin.cpython-312.pyc,,
sqlalchemy/ext/mypy/__pycache__/util.cpython-312.pyc,,
sqlalchemy/ext/mypy/apply.py,sha256=Aek_-XA1eXihT4attxhfE43yBKtCgsxBSb--qgZKUqc,10550
sqlalchemy/ext/mypy/decl_class.py,sha256=1vVJRII2apnLTUbc5HkJS6Z2GueaUv_eKvhbqh7Wik4,17384
sqlalchemy/ext/mypy/infer.py,sha256=KVnmLFEVS33Al8pUKI7MJbJQu3KeveBUMl78EluBORw,19369
sqlalchemy/ext/mypy/names.py,sha256=Q3ef8XQBgVm9WUwlItqlYCXDNi_kbV5DdLEgbtEMEI8,10479
sqlalchemy/ext/mypy/plugin.py,sha256=74ML8LI9xar0V86oCxnPFv5FQGEEfUzK64vOay4BKFs,9750
sqlalchemy/ext/mypy/util.py,sha256=1zuDQG8ezmF-XhJmAQU_lcBHiD--sL-lq20clg8t4lE,9448
sqlalchemy/ext/orderinglist.py,sha256=TGYbsGH72wEZcFNQDYDsZg9OSPuzf__P8YX8_2HtYUo,14384
sqlalchemy/ext/serializer.py,sha256=D0g4jMZkRk0Gjr0L-FZe81SR63h0Zs-9JzuWtT_SD7k,6140
sqlalchemy/future/__init__.py,sha256=q2mw-gxk_xoxJLEvRoyMha3vO1xSRHrslcExOHZwmPA,512
sqlalchemy/future/__pycache__/__init__.cpython-312.pyc,,
sqlalchemy/future/__pycache__/engine.cpython-312.pyc,,
sqlalchemy/future/engine.py,sha256=AgIw6vMsef8W6tynOTkxsjd6o_OQDwGjLdbpoMD8ue8,495
sqlalchemy/inspection.py,sha256=MF-LE358wZDUEl1IH8-Uwt2HI65EsQpQW5o5udHkZwA,5063
sqlalchemy/log.py,sha256=8x9UR3nj0uFm6or6bQF-JWb4fYv2zOeQjG_w-0wOJFA,8607
sqlalchemy/orm/__init__.py,sha256=ZYys5nL3RFUDCMOLFDBrRI52F6er3S1U1OY9TeORuKs,8463
sqlalchemy/orm/__pycache__/__init__.cpython-312.pyc,,
sqlalchemy/orm/__pycache__/_orm_constructors.cpython-312.pyc,,
sqlalchemy/orm/__pycache__/_typing.cpython-312.pyc,,
sqlalchemy/orm/__pycache__/attributes.cpython-312.pyc,,
sqlalchemy/orm/__pycache__/base.cpython-312.pyc,,
sqlalchemy/orm/__pycache__/bulk_persistence.cpython-312.pyc,,
sqlalchemy/orm/__pycache__/clsregistry.cpython-312.pyc,,
sqlalchemy/orm/__pycache__/collections.cpython-312.pyc,,
sqlalchemy/orm/__pycache__/context.cpython-312.pyc,,
sqlalchemy/orm/__pycache__/decl_api.cpython-312.pyc,,
sqlalchemy/orm/__pycache__/decl_base.cpython-312.pyc,,
sqlalchemy/orm/__pycache__/dependency.cpython-312.pyc,,
sqlalchemy/orm/__pycache__/descriptor_props.cpython-312.pyc,,
sqlalchemy/orm/__pycache__/dynamic.cpython-312.pyc,,
sqlalchemy/orm/__pycache__/evaluator.cpython-312.pyc,,
sqlalchemy/orm/__pycache__/events.cpython-312.pyc,,
sqlalchemy/orm/__pycache__/exc.cpython-312.pyc,,
sqlalchemy/orm/__pycache__/identity.cpython-312.pyc,,
sqlalchemy/orm/__pycache__/instrumentation.cpython-312.pyc,,
sqlalchemy/orm/__pycache__/interfaces.cpython-312.pyc,,
sqlalchemy/orm/__pycache__/loading.cpython-312.pyc,,
sqlalchemy/orm/__pycache__/mapped_collection.cpython-312.pyc,,
sqlalchemy/orm/__pycache__/mapper.cpython-312.pyc,,
sqlalchemy/orm/__pycache__/path_registry.cpython-312.pyc,,
sqlalchemy/orm/__pycache__/persistence.cpython-312.pyc,,
sqlalchemy/orm/__pycache__/properties.cpython-312.pyc,,
sqlalchemy/orm/__pycache__/query.cpython-312.pyc,,
sqlalchemy/orm/__pycache__/relationships.cpython-312.pyc,,
sqlalchemy/orm/__pycache__/scoping.cpython-312.pyc,,
sqlalchemy/orm/__pycache__/session.cpython-312.pyc,,
sqlalchemy/orm/__pycache__/state.cpython-312.pyc,,
sqlalchemy/orm/__pycache__/state_changes.cpython-312.pyc,,
sqlalchemy/orm/__pycache__/strategies.cpython-312.pyc,,
sqlalchemy/orm/__pycache__/strategy_options.cpython-312.pyc,,
sqlalchemy/orm/__pycache__/sync.cpython-312.pyc,,
sqlalchemy/orm/__pycache__/unitofwork.cpython-312.pyc,,
sqlalchemy/orm/__pycache__/util.cpython-312.pyc,,
sqlalchemy/orm/__pycache__/writeonly.cpython-312.pyc,,
sqlalchemy/orm/_orm_constructors.py,sha256=lVn8IZzU_Yq88you3qC8cnm4D1V9Wsf_LeEVTMX-QGc,100158
sqlalchemy/orm/_typing.py,sha256=DVBfpHmDVK4x1zxaGJPY2GoTrAsyR6uexv20Lzf1afc,4973
sqlalchemy/orm/attributes.py,sha256=wGpyY9aIxWRkL3y6qlbUgIW78rGA4X38ZhOaWI9S818,92535
sqlalchemy/orm/base.py,sha256=re6A8ooMxLyfCAeQfhBwwxeJQkiH7EtzzOZIVIiTPlw,27466
sqlalchemy/orm/bulk_persistence.py,sha256=wU204OQK-V6rJfpc3HPeMAA1X9vZg9nSNpI1zKuZtcY,70086
sqlalchemy/orm/clsregistry.py,sha256=29LyYiuj0qbebOpgW6DbBPNB2ikTweFQar1byCst7I0,17958
sqlalchemy/orm/collections.py,sha256=jpMsJGVixmrW9kfT8wevm9kpatKRqyDLcqWd7CjKPxE,52179
sqlalchemy/orm/context.py,sha256=pFFc-1ZjaV3aQMAO27UIh0W6NyLc30n2IbuWNprMtS8,112445
sqlalchemy/orm/decl_api.py,sha256=_WPKQ_vSE5k2TLtNmkaxxYmvbhZvkRMrrvCeDxdqDQE,63998
sqlalchemy/orm/decl_base.py,sha256=oeL7xrty2J81FyDx03dNe02BtZ-R6iYpum-BoA1rhik,82822
sqlalchemy/orm/dependency.py,sha256=hgjksUWhgbmgHK5GdJdiDCBgDAIGQXIrY-Tj79tbL2k,47631
sqlalchemy/orm/descriptor_props.py,sha256=pKtpP7H1LB_YuHRVrEYpfFZybEnUUdPwQXxduYFe2hA,37180
sqlalchemy/orm/dynamic.py,sha256=jksBDCOsm6EBMVParcNGuMeaAv12hX4IzouKspC-HPA,9786
sqlalchemy/orm/evaluator.py,sha256=q292K5vdpP69G7Z9y1RqI5GFAk2diUPwnsXE8De_Wgw,11925
sqlalchemy/orm/events.py,sha256=0lxP-EluVWSUr07ny2nsuQ8QhrDu9Qc4ON6QRUhuJnA,127703
sqlalchemy/orm/exc.py,sha256=IP40P-wOeXhkYk0YizuTC3wqm6W9cPTaQU08f5MMaQ0,7413
sqlalchemy/orm/identity.py,sha256=jHdCxCpCyda_8mFOfGmN_Pr0XZdKiU-2hFZshlNxbHs,9249
sqlalchemy/orm/instrumentation.py,sha256=M-kZmkUvHUxtf-0mCA8RIM5QmMH1hWlYR_pKMwaidjA,24321
sqlalchemy/orm/interfaces.py,sha256=Hmf1BjbfOarZRgMlruqghR7cgH2xyugA9v5t0x-a-wU,48502
sqlalchemy/orm/loading.py,sha256=9RacpzFOWbuKgPRWHFmyIvD4fYCLAnkpwBFASyQ2CoI,58277
sqlalchemy/orm/mapped_collection.py,sha256=3cneB1dfPTLrsTvKoo9_oCY2xtq4UAHfe5WSXPyqIS4,19690
sqlalchemy/orm/mapper.py,sha256=bfoRzNKKnjF-CDvr2Df7HZC9TepvtuQ49LRz_fW7DGQ,171088
sqlalchemy/orm/path_registry.py,sha256=sJZMv_WPqUpHfQtKWaX3WYFeKBcNJ8C3wOM2mkBGkTE,25920
sqlalchemy/orm/persistence.py,sha256=dzyB2JOXNwQgaCbN8kh0sEz00WFePr48qf8NWVCUZH8,61701
sqlalchemy/orm/properties.py,sha256=eDPFzxYUgdM3uWjHywnb1XW-i0tVKKyx7A2MCD31GQU,29306
sqlalchemy/orm/query.py,sha256=qKuFTUlbGVjs84AQ7APBY0PJhrzc2JJ1upeI658MV_o,117596
sqlalchemy/orm/relationships.py,sha256=dS5SY0v1MiD7iCNnAQlHaI6prUQhL5EkXT7ijc8FR8E,128644
sqlalchemy/orm/scoping.py,sha256=rJVc7_Lic4V00HZ-UvYFWkVpXqdrMayRmIs4fIwH1UA,78688
sqlalchemy/orm/session.py,sha256=aBBbHL08H-diS6UxGbySgitga6FCRWdN-EcpP3tPC4M,195261
sqlalchemy/orm/state.py,sha256=327-F4TG29s6mLC8oWRiO2PuvYIUZzY1MqUPjtUy7M4,37670
sqlalchemy/orm/state_changes.py,sha256=qKYg7NxwrDkuUY3EPygAztym6oAVUFcP2wXn7QD3Mz4,6815
sqlalchemy/orm/strategies.py,sha256=3fYRUlO61WKgxH2ZSn1kwkJyTRHZk7b9vlfCR4i1kXY,114994
sqlalchemy/orm/strategy_options.py,sha256=-n44NV8QX-3uWjhFMA_R_7vYy-d57gJY9snB9JeOj-w,85341
sqlalchemy/orm/sync.py,sha256=g7iZfSge1HgxMk9SKRgUgtHEbpbZ1kP_CBqOIdTOXqc,5779
sqlalchemy/orm/unitofwork.py,sha256=fiVaqcymbDDHRa1NjS90N9Z466nd5pkJOEi1dHO6QLY,27033
sqlalchemy/orm/util.py,sha256=JdroiJ788zPjrNdLYae_bj0hsxzEDJjLbpUMwqonLCU,80728
sqlalchemy/orm/writeonly.py,sha256=SYu2sAaHZONk2pW4PmtE871LG-O0P_bjidvKzY1H_zI,22305
sqlalchemy/pool/__init__.py,sha256=qiDdq4r4FFAoDrK6ncugF_i6usi_X1LeJt-CuBHey0s,1804
sqlalchemy/pool/__pycache__/__init__.cpython-312.pyc,,
sqlalchemy/pool/__pycache__/base.cpython-312.pyc,,
sqlalchemy/pool/__pycache__/events.cpython-312.pyc,,
sqlalchemy/pool/__pycache__/impl.cpython-312.pyc,,
sqlalchemy/pool/base.py,sha256=WF4az4ZKuzQGuKeSJeyexaYjmWZUvYdC6KIi8zTGodw,52236
sqlalchemy/pool/events.py,sha256=xGjkIUZl490ZDtCHqnQF9ZCwe2Jv93eGXmnQxftB11E,13147
sqlalchemy/pool/impl.py,sha256=JwpALSkH-pCoO_6oENbkHYY00Jx9nlttyoI61LivRNc,18944
sqlalchemy/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
sqlalchemy/schema.py,sha256=dKiWmgHYjcKQ4TiiD6vD0UMmIsD8u0Fsor1M9AAeGUs,3194
sqlalchemy/sql/__init__.py,sha256=UNa9EUiYWoPayf-FzNcwVgQvpsBdInPZfpJesAStN9o,5820
sqlalchemy/sql/__pycache__/__init__.cpython-312.pyc,,
sqlalchemy/sql/__pycache__/_dml_constructors.cpython-312.pyc,,
sqlalchemy/sql/__pycache__/_elements_constructors.cpython-312.pyc,,
sqlalchemy/sql/__pycache__/_orm_types.cpython-312.pyc,,
sqlalchemy/sql/__pycache__/_py_util.cpython-312.pyc,,
sqlalchemy/sql/__pycache__/_selectable_constructors.cpython-312.pyc,,
sqlalchemy/sql/__pycache__/_typing.cpython-312.pyc,,
sqlalchemy/sql/__pycache__/annotation.cpython-312.pyc,,
sqlalchemy/sql/__pycache__/base.cpython-312.pyc,,
sqlalchemy/sql/__pycache__/cache_key.cpython-312.pyc,,
sqlalchemy/sql/__pycache__/coercions.cpython-312.pyc,,
sqlalchemy/sql/__pycache__/compiler.cpython-312.pyc,,
sqlalchemy/sql/__pycache__/crud.cpython-312.pyc,,
sqlalchemy/sql/__pycache__/ddl.cpython-312.pyc,,
sqlalchemy/sql/__pycache__/default_comparator.cpython-312.pyc,,
sqlalchemy/sql/__pycache__/dml.cpython-312.pyc,,
sqlalchemy/sql/__pycache__/elements.cpython-312.pyc,,
sqlalchemy/sql/__pycache__/events.cpython-312.pyc,,
sqlalchemy/sql/__pycache__/expression.cpython-312.pyc,,
sqlalchemy/sql/__pycache__/functions.cpython-312.pyc,,
sqlalchemy/sql/__pycache__/lambdas.cpython-312.pyc,,
sqlalchemy/sql/__pycache__/naming.cpython-312.pyc,,
sqlalchemy/sql/__pycache__/operators.cpython-312.pyc,,
sqlalchemy/sql/__pycache__/roles.cpython-312.pyc,,
sqlalchemy/sql/__pycache__/schema.cpython-312.pyc,,
sqlalchemy/sql/__pycache__/selectable.cpython-312.pyc,,
sqlalchemy/sql/__pycache__/sqltypes.cpython-312.pyc,,
sqlalchemy/sql/__pycache__/traversals.cpython-312.pyc,,
sqlalchemy/sql/__pycache__/type_api.cpython-312.pyc,,
sqlalchemy/sql/__pycache__/util.cpython-312.pyc,,
sqlalchemy/sql/__pycache__/visitors.cpython-312.pyc,,
sqlalchemy/sql/_dml_constructors.py,sha256=YdBJex0MCVACv4q2nl_ii3uhxzwU6aDB8zAsratX5UQ,3867
sqlalchemy/sql/_elements_constructors.py,sha256=1SX6o1ezeB8C9DAa2m0WxmfhM3ji3FeCprXFQkNerNY,63048
sqlalchemy/sql/_orm_types.py,sha256=T-vjcry4C1y0GToFKVxQCnmly_-Zsq4IO4SHN6bvUF4,625
sqlalchemy/sql/_py_util.py,sha256=hiM9ePbRSGs60bAMxPFuJCIC_p9SQ1VzqXGiPchiYwE,2173
sqlalchemy/sql/_selectable_constructors.py,sha256=wjE6HrLm9cR7bxvZXT8sFLUqT6t_J9G1XyQCnYmBDl0,18780
sqlalchemy/sql/_typing.py,sha256=oqwrYHVMtK-AuKGH9c4SgfiOEJUt5vjkzSEzzscMHkM,12771
sqlalchemy/sql/annotation.py,sha256=aqbbVz9kfbCT3_66CZ9GEirVN197Cukoqt8rq48FgkQ,18245
sqlalchemy/sql/base.py,sha256=Lg7iHywXmB7XuRMar45XSM4KayChGhfj1c8E7nWUJdo,73899
sqlalchemy/sql/cache_key.py,sha256=0Db8mR8IrpBgdzXs4TGTt98LOpL3c7KABd72MAPKUQQ,33668
sqlalchemy/sql/coercions.py,sha256=1xzN_9U5BCJGgokdc5iYj5o2cMAfEEZkr1Oa9Q-JYj8,40493
sqlalchemy/sql/compiler.py,sha256=e5XWUmeZnE3O8r7fndfmXjAze3qlFZY223BHqrso0AY,274647
sqlalchemy/sql/crud.py,sha256=g9xcol2KRGjZi1qsb2-bVz8zgVy_53gfMtJcnNO2vyQ,56521
sqlalchemy/sql/ddl.py,sha256=CIqMilCKfuQnF0lrZsQdTxgrbXqcTauKr0Ojzj77PFQ,45602
sqlalchemy/sql/default_comparator.py,sha256=utXWsZVGEjflhFfCT4ywa6RnhORc1Rryo87Hga71Rps,16707
sqlalchemy/sql/dml.py,sha256=pn0Lm1ofC5qVZzwGWFW73lPCiNba8OsTeemurJgwRyg,65614
sqlalchemy/sql/elements.py,sha256=XpfBuu4sh5W5lHHUZN95DTl4FM4MM_zs0GKqHZvdXXM,176146
sqlalchemy/sql/events.py,sha256=iC_Q1Htm1Aobt5tOYxWfHHqNpoytrULORmUKcusH_-E,18290
sqlalchemy/sql/expression.py,sha256=VMX-dLpsZYnVRJpYNDozDUgaj7iQ0HuewUKVefD57PE,7586
sqlalchemy/sql/functions.py,sha256=kMMYplvuIHFAPwxBI03SizwaLcYEHzysecWk-R1V-JM,63762
sqlalchemy/sql/lambdas.py,sha256=DP0Qz7Ypo8QhzMwygGHYgRhwJMx-rNezO1euouH3iYU,49292
sqlalchemy/sql/naming.py,sha256=ZHs1qSV3ou8TYmZ92uvU3sfdklUQlIz4uhe330n05SU,6858
sqlalchemy/sql/operators.py,sha256=r4oQp4h5zTMFFOpiFNV56joIK-QIjJCobatsmaZ-724,75935
sqlalchemy/sql/roles.py,sha256=pOsVn_OZD7mF2gJByHf24Rjopt0_Hu3dUCEOK5t4KS8,7662
sqlalchemy/sql/schema.py,sha256=-5QrCcIJNbs1Et6oRzfpTDvhP4R3KbRJkGiYuJExayI,229767
sqlalchemy/sql/selectable.py,sha256=gAOLh2hq0xRVqAaO6ctQr9cgLgdz40iCEhCJ_Gp49cE,233587
sqlalchemy/sql/sqltypes.py,sha256=d3xNVCDPV2MaPInKWvIpLDGS8NwGFLP614_dWws3B_U,126808
sqlalchemy/sql/traversals.py,sha256=NFgJrVJzInO3HrnG90CklxrDXhFydZohPs2vRJkh3Bo,33589
sqlalchemy/sql/type_api.py,sha256=CJAvRix_rRoOsE77sH4BqeGX0lIdtk8dX-StjGx3mUo,83208
sqlalchemy/sql/util.py,sha256=qGHQF-tPCj-m1FBerzT7weCanGcXU7dK5m-W7NHio-4,48077
sqlalchemy/sql/visitors.py,sha256=71wdVvhhZL4nJvVwFAs6ssaW-qZgNRSmKjpAcOzF_TA,36317
sqlalchemy/testing/__init__.py,sha256=VsrEHrORpAF5n7Vfl43YQgABh6EP1xBx_gHxs7pSXeE,3126
sqlalchemy/testing/__pycache__/__init__.cpython-312.pyc,,
sqlalchemy/testing/__pycache__/assertions.cpython-312.pyc,,
sqlalchemy/testing/__pycache__/assertsql.cpython-312.pyc,,
sqlalchemy/testing/__pycache__/asyncio.cpython-312.pyc,,
sqlalchemy/testing/__pycache__/config.cpython-312.pyc,,
sqlalchemy/testing/__pycache__/engines.cpython-312.pyc,,
sqlalchemy/testing/__pycache__/entities.cpython-312.pyc,,
sqlalchemy/testing/__pycache__/exclusions.cpython-312.pyc,,
sqlalchemy/testing/__pycache__/pickleable.cpython-312.pyc,,
sqlalchemy/testing/__pycache__/profiling.cpython-312.pyc,,
sqlalchemy/testing/__pycache__/provision.cpython-312.pyc,,
sqlalchemy/testing/__pycache__/requirements.cpython-312.pyc,,
sqlalchemy/testing/__pycache__/schema.cpython-312.pyc,,
sqlalchemy/testing/__pycache__/util.cpython-312.pyc,,
sqlalchemy/testing/__pycache__/warnings.cpython-312.pyc,,
sqlalchemy/testing/assertions.py,sha256=gL0rA7CCZJbcVgvWOPV91tTZTRwQc1_Ta0-ykBn83Ew,31439
sqlalchemy/testing/assertsql.py,sha256=IgQG7l94WaiRP8nTbilJh1ZHZl125g7GPq-S5kmQZN0,16817
sqlalchemy/testing/asyncio.py,sha256=kM8uuOqDBagZF0r9xvGmsiirUVLUQ_KBzjUFU67W-b8,3830
sqlalchemy/testing/config.py,sha256=AqyH1qub_gDqX0BvlL-JBQe7N-t2wo8655FtwblUNOY,12090
sqlalchemy/testing/engines.py,sha256=HFJceEBD3Q_TTFQMTtIV5wGWO_a7oUgoKtUF_z636SM,13481
sqlalchemy/testing/entities.py,sha256=IphFegPKbff3Un47jY6bi7_MQXy6qkx_50jX2tHZJR4,3354
sqlalchemy/testing/exclusions.py,sha256=T8B01hmm8WVs-EKcUOQRzabahPqblWJfOidi6bHJ6GA,12460
sqlalchemy/testing/fixtures/__init__.py,sha256=dMClrIoxqlYIFpk2ia4RZpkbfxsS_3EBigr9QsPJ66g,1198
sqlalchemy/testing/fixtures/__pycache__/__init__.cpython-312.pyc,,
sqlalchemy/testing/fixtures/__pycache__/base.cpython-312.pyc,,
sqlalchemy/testing/fixtures/__pycache__/mypy.cpython-312.pyc,,
sqlalchemy/testing/fixtures/__pycache__/orm.cpython-312.pyc,,
sqlalchemy/testing/fixtures/__pycache__/sql.cpython-312.pyc,,
sqlalchemy/testing/fixtures/base.py,sha256=9r_J2ksiTzClpUxW0TczICHrWR7Ny8PV8IsBz6TsGFI,12256
sqlalchemy/testing/fixtures/mypy.py,sha256=gdxiwNFIzDlNGSOdvM3gbwDceVCC9t8oM5kKbwyhGBk,11973
sqlalchemy/testing/fixtures/orm.py,sha256=8EFbnaBbXX_Bf4FcCzBUaAHgyVpsLGBHX16SGLqE3Fg,6095
sqlalchemy/testing/fixtures/sql.py,sha256=TE5q2BSOc_Vq1TPaLnj1F9ZA_YiQHlqyvUtGId9bGr0,15774
sqlalchemy/testing/pickleable.py,sha256=U9mIqk-zaxq9Xfy7HErP7UrKgTov-A3QFnhZh-NiOjI,2833
sqlalchemy/testing/plugin/__init__.py,sha256=79F--BIY_NTBzVRIlJGgAY5LNJJ3cD19XvrAo4X0W9A,247
sqlalchemy/testing/plugin/__pycache__/__init__.cpython-312.pyc,,
sqlalchemy/testing/plugin/__pycache__/bootstrap.cpython-312.pyc,,
sqlalchemy/testing/plugin/__pycache__/plugin_base.cpython-312.pyc,,
sqlalchemy/testing/plugin/__pycache__/pytestplugin.cpython-312.pyc,,
sqlalchemy/testing/plugin/bootstrap.py,sha256=oYScMbEW4pCnWlPEAq1insFruCXFQeEVBwo__i4McpU,1685
sqlalchemy/testing/plugin/plugin_base.py,sha256=BgNzWNEmgpK4CwhyblQQKnH-7FDKVi_Uul5vw8fFjBU,21578
sqlalchemy/testing/plugin/pytestplugin.py,sha256=6jkQHH2VQMD75k2As9CuWXmEy9jrscoFRhCNg6-PaTw,27656
sqlalchemy/testing/profiling.py,sha256=PbuPhRFbauFilUONeY3tV_Y_5lBkD7iCa8VVyH2Sk9Y,10148
sqlalchemy/testing/provision.py,sha256=zXsw2D2Xpmw_chmYLsE1GXQqKQ-so3V8xU_joTcKan0,14619
sqlalchemy/testing/requirements.py,sha256=N9pSj7z2wVMkBif-DQfPVa_cl9k6p9g_J5FY1OsWtrY,51817
sqlalchemy/testing/schema.py,sha256=lr4GkGrGwagaHMuSGzWdzkMaj3HnS7dgfLLWfxt__-U,6513
sqlalchemy/testing/suite/__init__.py,sha256=Y5DRNG0Yl1u3ypt9zVF0Z9suPZeuO_UQGLl-wRgvTjU,722
sqlalchemy/testing/suite/__pycache__/__init__.cpython-312.pyc,,
sqlalchemy/testing/suite/__pycache__/test_cte.cpython-312.pyc,,
sqlalchemy/testing/suite/__pycache__/test_ddl.cpython-312.pyc,,
sqlalchemy/testing/suite/__pycache__/test_deprecations.cpython-312.pyc,,
sqlalchemy/testing/suite/__pycache__/test_dialect.cpython-312.pyc,,
sqlalchemy/testing/suite/__pycache__/test_insert.cpython-312.pyc,,
sqlalchemy/testing/suite/__pycache__/test_reflection.cpython-312.pyc,,
sqlalchemy/testing/suite/__pycache__/test_results.cpython-312.pyc,,
sqlalchemy/testing/suite/__pycache__/test_rowcount.cpython-312.pyc,,
sqlalchemy/testing/suite/__pycache__/test_select.cpython-312.pyc,,
sqlalchemy/testing/suite/__pycache__/test_sequence.cpython-312.pyc,,
sqlalchemy/testing/suite/__pycache__/test_types.cpython-312.pyc,,
sqlalchemy/testing/suite/__pycache__/test_unicode_ddl.cpython-312.pyc,,
sqlalchemy/testing/suite/__pycache__/test_update_delete.cpython-312.pyc,,
sqlalchemy/testing/suite/test_cte.py,sha256=6zBC3W2OwX1Xs-HedzchcKN2S7EaLNkgkvV_JSZ_Pq0,6451
sqlalchemy/testing/suite/test_ddl.py,sha256=1Npkf0C_4UNxphthAGjG078n0vPEgnSIHpDu5MfokxQ,12031
sqlalchemy/testing/suite/test_deprecations.py,sha256=BcJxZTcjYqeOAENVElCg3hVvU6fkGEW3KGBMfnW8bng,5337
sqlalchemy/testing/suite/test_dialect.py,sha256=EH4ZQWbnGdtjmx5amZtTyhYmrkXJCvW1SQoLahoE7uk,22923
sqlalchemy/testing/suite/test_insert.py,sha256=9azifj6-OCD7s8h_tAO1uPw100ibQv8YoKc_VA3hn3c,18824
sqlalchemy/testing/suite/test_reflection.py,sha256=tJSbJFg5fw0sSUv3I_FPmhN7rWWeJtq3YyxmylWJUlM,106466
sqlalchemy/testing/suite/test_results.py,sha256=NQ23m8FDVd0ub751jN4PswGoAhk5nrqvjHvpYULZXnc,15937
sqlalchemy/testing/suite/test_rowcount.py,sha256=3KDTlRgjpQ1OVfp__1cv8Hvq4CsDKzmrhJQ_WIJWoJg,7900
sqlalchemy/testing/suite/test_select.py,sha256=FvMFYQW9IJpDWGYZiJk46is6YrtmdSghBdTjZCG8T0Y,58574
sqlalchemy/testing/suite/test_sequence.py,sha256=66bCoy4xo99GBSaX6Hxb88foANAykLGRz1YEKbvpfuA,9923
sqlalchemy/testing/suite/test_types.py,sha256=rFmTOg6XuMch9L2-XthfLJRCTTwpZbMfrNss2g09gmc,65677
sqlalchemy/testing/suite/test_unicode_ddl.py,sha256=c3_eIxLyORuSOhNDP0jWKxPyUf3SwMFpdalxtquwqlM,6141
sqlalchemy/testing/suite/test_update_delete.py,sha256=yTiM2unnfOK9rK8ZkqeTTU_MkT-RsKFLmdYliniZfAY,3994
sqlalchemy/testing/util.py,sha256=BFiSp3CEX95Dr-vv4l_7ZRu5vjZi9hjjnp-JKNfuS5E,14080
sqlalchemy/testing/warnings.py,sha256=fJ-QJUY2zY2PPxZJKv9medW-BKKbCNbA4Ns_V3YwFXM,1546
sqlalchemy/types.py,sha256=cQFM-hFRmaf1GErun1qqgEs6QxufvzMuwKqj9tuMPpE,3168
sqlalchemy/util/__init__.py,sha256=B3bedg-LSQEscwqgmYYU-VENUX8_zAE3q9vb7tkfJNY,8277
sqlalchemy/util/__pycache__/__init__.cpython-312.pyc,,
sqlalchemy/util/__pycache__/_collections.cpython-312.pyc,,
sqlalchemy/util/__pycache__/_concurrency_py3k.cpython-312.pyc,,
sqlalchemy/util/__pycache__/_has_cy.cpython-312.pyc,,
sqlalchemy/util/__pycache__/_py_collections.cpython-312.pyc,,
sqlalchemy/util/__pycache__/compat.cpython-312.pyc,,
sqlalchemy/util/__pycache__/concurrency.cpython-312.pyc,,
sqlalchemy/util/__pycache__/deprecations.cpython-312.pyc,,
sqlalchemy/util/__pycache__/langhelpers.cpython-312.pyc,,
sqlalchemy/util/__pycache__/preloaded.cpython-312.pyc,,
sqlalchemy/util/__pycache__/queue.cpython-312.pyc,,
sqlalchemy/util/__pycache__/tool_support.cpython-312.pyc,,
sqlalchemy/util/__pycache__/topological.cpython-312.pyc,,
sqlalchemy/util/__pycache__/typing.cpython-312.pyc,,
sqlalchemy/util/_collections.py,sha256=aZoSAVOXnHBoYEsxDOi0O9odg9wqLbGb7PGjaWQKiyY,20078
sqlalchemy/util/_concurrency_py3k.py,sha256=zb0Bow2Y_QjTdaACEviBEEaFvqDuVvpJfmwCjaw8xNE,9170
sqlalchemy/util/_has_cy.py,sha256=wCQmeSjT3jaH_oxfCEtGk-1g0gbSpt5MCK5UcWdMWqk,1247
sqlalchemy/util/_py_collections.py,sha256=U6L5AoyLdgSv7cdqB4xxQbw1rpeJjyOZVXffgxgga8I,16714
sqlalchemy/util/compat.py,sha256=R6bpBydldtbr6h7oJePihQxFb7jKiI-YDsK465MSOzk,8714
sqlalchemy/util/concurrency.py,sha256=9lT_cMoO1fZNdY8QTUZ22oeSf-L5I-79Ke7chcBNPA0,3304
sqlalchemy/util/deprecations.py,sha256=YBwvvYhSB8LhasIZRKvg_-WNoVhPUcaYI1ZrnjDn868,11971
sqlalchemy/util/langhelpers.py,sha256=5XpX5X6sqKtZ6B07WmlP2oOCbUn7CptBNVTe4LR7jqA,64957
sqlalchemy/util/preloaded.py,sha256=az7NmLJLsqs0mtM9uBkIu10-841RYDq8wOyqJ7xXvqE,5904
sqlalchemy/util/queue.py,sha256=CaeSEaYZ57YwtmLdNdOIjT5PK_LCuwMFiO0mpp39ybM,10185
sqlalchemy/util/tool_support.py,sha256=9braZyidaiNrZVsWtGmkSmus50-byhuYrlAqvhjcmnA,6135
sqlalchemy/util/topological.py,sha256=N3M3Le7KzGHCmqPGg0ZBqixTDGwmFLhOZvBtc4rHL_g,3458
sqlalchemy/util/typing.py,sha256=UctfgugSTnFJgZraikXJbB-5RoV7oyccZmbf2NeRfoA,16671
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Do not commit virtual environment files to version control.

The .venv/ directory contains auto-generated package installation artifacts and must be excluded from the repository. Committing these files:

  • Bloats the repository with thousands of unnecessary files
  • Creates merge conflicts when developers use different environments
  • Breaks portability across platforms and Python versions

Remove these files and add .venv/ to .gitignore.

#!/bin/bash
# Check if .venv is already in .gitignore
if [ -f .gitignore ]; then
  echo "=== Checking .gitignore for .venv exclusion ==="
  grep -E '^\s*\.?venv' .gitignore || echo ".venv is NOT in .gitignore"
else
  echo ".gitignore file does not exist"
fi

# Check how many .venv files are staged
echo ""
echo "=== Counting .venv files in repository ==="
fd . .venv --type f 2>/dev/null | wc -l || echo "Could not count .venv files"
🤖 Prompt for AI Agents
In .venv/lib/python3.12/site-packages/SQLAlchemy-2.0.31.dist-info/RECORD lines
1-530: the review flags that the entire .venv directory (including this RECORD
file) must not be committed; remove the committed virtualenv files and ensure
.venv is ignored. Fix by removing the .venv files from the repository
history/staging (e.g. git rm -r --cached .venv or remove and restore from a
clean branch), add a rule for .venv to .gitignore (ensure pattern matches your
venv name), and commit the .gitignore change and the removal commit so .venv is
no longer tracked.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
.gitignore (1)

1-9: Enhance .gitignore with additional Python project patterns.

The .gitignore covers basic virtual environment and cache patterns, but is missing several common Python project artifacts. Consider adding patterns for:

  • Additional bytecode formats: *.pyo, *.pyd
  • Distribution/build artifacts: build/, dist/, *.egg-info/, .eggs/
  • Testing: .pytest_cache/, .coverage, htmlcov/, .tox/
  • Python metadata: .Python
  • IDE/editor configs: .vscode/, .idea/, *.swp, *.swo, *~
  • OS files: .DS_Store

Consider applying this enhanced .gitignore:

 # Virtual environments
 .venv/
 venv/
 ENV/
 env/
 
 # Python
 __pycache__/
 *.pyc
+*.pyo
+*.pyd
+.Python
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+
+# Testing
+.pytest_cache/
+.coverage
+htmlcov/
+.tox/
+
+# IDE
+.vscode/
+.idea/
+*.iml
+*.swp
+*.swo
+*~
+
+# OS
+.DS_Store
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 962ad1b and fd8435a.

📒 Files selected for processing (4)
  • .gitignore (1 hunks)
  • .venv/bin/python (0 hunks)
  • .venv/bin/python3 (0 hunks)
  • .venv/bin/python3.12 (0 hunks)
💤 Files with no reviewable changes (3)
  • .venv/bin/python3.12
  • .venv/bin/python
  • .venv/bin/python3
🔇 Additional comments (1)
.gitignore (1)

1-9: Discrepancy: PR objectives mention time_tracker.py but only .gitignore is provided.

The PR objectives describe adding a new time_tracker.py file with start_timer() and finish_timer() functions under app/core/. However, only .gitignore is included in the provided files for review. Please confirm whether time_tracker.py should also be included in this review.

@felixjordandev felixjordandev merged commit f077333 into main Dec 3, 2025
1 check passed
@felixjordandev felixjordandev deleted the feat/20251203143530 branch December 3, 2025 16:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants