Skip to content

Conversation

@marclove
Copy link
Owner

@marclove marclove commented Sep 9, 2025

This pull request modernizes the project's development, CI, and documentation workflows by migrating from Hatch to the uv toolchain, introducing automated lint/type checks with pre-commit, and improving developer and contributor documentation. It also updates the documentation to reflect new async iterator features and exposes key API types and errors for easier imports.

Tooling & CI Modernization:

  • Switched from Hatch to uv for dependency management and scripts throughout the project. All workflow files and documentation now use uv commands for setup, linting, type checking, and testing. (.github/workflows/ci.yml, .github/workflows/documentation.yml, .prototools, pyproject.toml, README.md, AGENTS.md, CLAUDE.md) [1] [2] [3] [4] [5] [6] [7]
  • Removed the old Hatch-based workflow and all related configuration from the repository, simplifying environment management. (.github/workflows/hatch.yml, pyproject.toml) [1] [2]

Developer Experience Improvements:

  • Added pre-commit configuration for automated linting (ruff) and type checking (pyright). (.pre-commit-config.yaml)
  • Added a .python-version file to standardize on Python 3.12 for development.
  • Updated and expanded contributor/developer guidelines in AGENTS.md and added a detailed architecture and workflow guide for Claude in CLAUDE.md. [1] [2]

Documentation Enhancements:

  • Updated the documentation to describe new async iterator features (threads_iter, replies_iter), per-call API options, and usage of Pydantic models for response validation. (README.md, docs/source/pythreads.rst) [1] [2] [3]
  • Added new documentation pages for architecture and doctests, and enabled Sphinx doctest extension. (docs/source/architecture.rst, docs/source/doctests.rst, docs/source/conf.py, docs/source/index.rst) [1] [2] [3] [4]

API Improvements:

  • The pythreads.api package now re-exports key types and errors, making it easier to import these directly from pythreads.api. (src/pythreads/api/__init__.py)

Configuration Updates:

  • Added a [tool.pyright] section to pyproject.toml for consistent type checking configuration.

References: [1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12] [13] [14] [15] [16] [17] [18] [19]

BREAKING CHANGE: Development workflow now uses uv instead of Hatch

- Replace Hatch CI workflow with new uv-based CI pipeline
- Update documentation workflow to use uv instead of Hatch
- Remove old Hatch workflow configuration
- Add uv configuration files (.prototools, .python-version)
- Update pyproject.toml to include dev dependencies and remove Hatch environments
- Add comprehensive development guidelines in AGENTS.md
- Fix type annotation issues in API insights method
- Update smoke test documentation to reference new uv commands

This change requires developers to install and use uv instead of Hatch for development tasks. All previous Hatch commands need to be replaced with their uv equivalents as documented in AGENTS.md.
Add /llmc.toml to the .gitignore file to prevent tracking of the LLMC configuration file in version control.
- Add ThreadsHTTPError exception for better HTTP error handling
- Add Python <3.11 compatibility fallback for StrEnum
- Add logging support with debug messages for failed requests
- Make SSL credentials optional for development environments
- Add 30-second timeout to HTTP client sessions
- Improve JSON parsing with UTC timezone support for ISO-8601 dates
- Consolidate HTTP methods into unified _request method
- Remove unused fields from default field sets
- Fix parameter name bug in user_insights method (PARAMS__METRIC -> PARAMS__FIELDS)
- Improve URL parameter encoding to handle non-string values

BREAKING CHANGE: SSL certificates are now optional, which may affect existing deployments that rely on the previous behavior of throwing an error when certificates are missing. The ThreadsHTTPError exception replaces generic exceptions for HTTP errors, which may affect error handling in client code.
Split the monolithic api.py file into multiple modules:
- client.py: Contains the main API class with all methods
- types.py: Contains all type definitions, enums, and constants
- errors.py: Contains all custom exception classes
- __init__.py: Provides backwards-compatible imports

This change improves code maintainability and readability by separating concerns. The API interface remains the same with all classes and functions re-exported from the api module.

BREAKING CHANGE: Internal import paths have changed. Code importing specific classes directly from pythreads.api.Field, pythreads.api.MediaType, etc. may break. Use imports from pythreads.api instead.
- Update GitHub Actions workflow to test against Python versions 3.8, 3.9, 3.10, 3.11, and 3.12
- Add matrix strategy for comprehensive version coverage
- Pin Python version using uv for consistent environment setup
- Add test coverage for ThreadsHTTPError exception handling in API methods
- Add test for credentials deserialization with Z-suffix timezone format
- Add test for SSL credentials when environment variables are missing

These changes improve CI robustness by ensuring compatibility across supported Python versions and enhance test coverage for error handling scenarios.
Add timeout and base_url parameters to the API client constructor to allow customization of request timeout and API endpoint. This enhances flexibility for different deployment environments and network conditions.

- Add optional timeout parameter (default 30s) supporting float or ClientTimeout objects
- Add optional base_url parameter for custom API endpoints
- Refactor URL building into _build_url method for consistent base_url handling
- Add pre-commit configuration with ruff and pyright hooks
- Update README with development section including setup and testing instructions
- Add test coverage for HTTP error handling with text bodies

The API constructor now accepts keyword-only timeout and base_url parameters while maintaining backward compatibility with existing code.
- Add retry configuration parameters: retries, backoff_base, backoff_max
- Implement automatic retry for transient HTTP errors (429, 5xx status codes)
- Add exponential backoff with jitter to prevent thundering herd
- Update API class docstring and documentation with new options
- Add comprehensive logging for retry attempts

The retry mechanism helps improve reliability when dealing with rate limiting
and temporary server errors, while the exponential backoff prevents
overwhelming the server with rapid retry attempts.
Extract HTTP transport logic and accounts endpoints into dedicated service classes:

- Add Transport class to centralize HTTP request handling, retries, and URL building
- Add AccountsService to encapsulate account-related API operations
- Wire Transport and AccountsService instances in API client initialization
- Migrate account() and publishing_limit() methods to use new service layer
- Add types-requests dependency for improved type checking
- Exclude tests directory from pyright type checking to focus on source code

The existing public API remains unchanged - this is an internal refactoring to improve code organization and maintainability.
Move threads, replies, and conversation methods from the main API client
to a new ThreadsService class to improve code organization and separation
of concerns.

Changes:
- Create new ThreadsService class in endpoints/threads.py
- Add threads_service property to API client
- Refactor threads(), replies(), and conversation() methods to delegate
  to ThreadsService
- Maintain existing public API interface for backwards compatibility
Extract media container creation, publishing, and status checking operations from the main API client into a new MediaService class. This improves code organization and separation of concerns by moving media-related functionality into its own service module.

- Add new MediaService class in endpoints/media.py
- Refactor create_container, create_carousel_container, container_status, publish_container, container, and thread methods to delegate to MediaService
- Update API class to initialize and use MediaService instance
- Maintain backward compatibility with existing API interface
…ervices

- Create InsightsService to handle user insights and thread insights functionality
- Create ModerationService to handle reply management operations
- Move user_insights, insights, and manage_reply methods from API class to respective services
- Update API class to initialize and use the new services
- Maintain backward compatibility by keeping the same public API methods

This refactoring improves code organization by separating concerns into dedicated service classes while preserving the existing API interface.
Add comprehensive type annotations using TypedDict for API responses:
- AccountResponse for account endpoint return values
- PublishingLimitResponse for publishing limit data
- InsightsResponse for insights API responses

Update method signatures in client.py and endpoint modules to use proper
return type annotations instead of 'Any'. Add documentation section for
endpoint modules in pythreads.rst with automodule directives for better
API reference coverage.

These changes improve type safety and developer experience without breaking
existing functionality.
Add convenience async iterators to simplify pagination:
- threads_iter: iterate through user's threads with automatic cursor handling
- replies_iter: iterate through thread replies
- conversation_iter: iterate through flattened conversations

Additional improvements:
- Add architecture documentation explaining package layout and design principles
- Extract utility functions for timestamp and date parameter handling
- Update README with iterator usage examples
- Document iterator methods in API reference

All iterators support per_page limits and optional page_limit for bounded iteration.
Add support for per-request customization through a new `request_options` parameter across all API client methods. This allows users to override default retry behavior, timeouts, backoff settings, and base URL on a per-request basis.

Changes include:
- Add `request_options` parameter to all client methods (account, user_insights, publishing_limit, etc.)
- Add `RequestOptions` TypedDict type for type safety
- Update transport layer to handle request-specific options
- Maintain backward compatibility by making the parameter optional with default None
- Support customizable retries, backoff_base, backoff_max, base_url, and timeout settings

This enhancement provides greater flexibility for handling different API call requirements without changing global client configuration.
Add optional Pydantic v2 models for API response validation with a new `models` extra. Users can now install with `pip install pythreads[models]` to get validation models for insights and other API responses.

Changes:
- Add `pythreads.api.models` module with Pydantic models for account, publishing limits, and insights responses
- Add `models` optional dependency group in pyproject.toml requiring pydantic>=2,<3
- Update documentation with usage examples for the new models
- Make python-dotenv import optional with fallback to avoid hard dependency
- Add per-call request options example in README
…test

- Add comprehensive docstrings to all public API methods in client.py
- Document parameters, return types, and usage for each method
- Include details about optional request_options parameter
- Create new test file for validating InsightsResponseModel Pydantic validation

This improves API documentation and ensures model validation works correctly.
- Enable sphinx.ext.doctest extension in documentation configuration
- Create new doctests.rst file with examples for utils and pydantic models
- Add doctests section to main documentation index
- Enhance pythreads.rst with per-call overrides and common methods examples

This addition provides executable documentation examples that validate
core functionality while serving as practical usage guides for users.
- Add CLAUDE.md documentation for AI assistant guidance
- Introduce centralized Config class for environment variable management
- Add comprehensive response type definitions for better type safety
- Refactor pagination with reusable PaginatedIterator base class
- Replace manual iterator implementations with specialized iterator classes
- Add proper return type annotations across API methods
- Maintain backward compatibility with legacy get_ssl_credentials function

The Config class centralizes all environment variable handling and validation,
improving maintainability and testing. New TypedDict definitions provide
better IDE support and runtime type checking for API responses. The paginated
iterator refactoring reduces code duplication while maintaining the same
async iterator interface.
Copilot AI review requested due to automatic review settings September 9, 2025 22:16
@marclove marclove self-assigned this Sep 9, 2025
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This pull request modernizes the project's development, CI, and documentation workflows by migrating from Hatch to the uv toolchain, introducing automated lint/type checks with pre-commit, and improving developer and contributor documentation. It also updates the documentation to reflect new async iterator features and exposes key API types and errors for easier imports.

  • Replaced Hatch-based build system with uv for faster dependency management and simplified scripts
  • Added comprehensive API refactoring with service-oriented architecture and async iterators
  • Enhanced documentation with new architectural guides and per-call API options

Reviewed Changes

Copilot reviewed 36 out of 38 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
tests/test_threads.py Added new test for SSL credentials handling and updated imports
tests/test_smoke.py Updated test command from Hatch to uv
tests/test_models.py New test file for Pydantic model validation
tests/test_credentials.py Added test for UTC timezone handling in credentials deserialization
tests/test_api.py Added comprehensive HTTP error handling tests
src/pythreads/threads.py Refactored configuration loading and SSL handling to use centralized config
src/pythreads/credentials.py Enhanced ISO timestamp parsing to support 'Z' UTC suffix
src/pythreads/config.py New centralized configuration management system
src/pythreads/api/ Complete API restructure with service-oriented architecture and async iterators
pyproject.toml Migration from Hatch to uv with new dev dependencies and Pyright configuration
docs/ Enhanced documentation with new architecture guides and API usage examples
CI/Workflows Updated from Hatch to uv-based workflows

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Comment on lines +9 to +13
try: # pragma: no cover - import path when available
from enum import StrEnum # type: ignore[attr-defined]
except Exception: # pragma: no cover - fallback for older Python
class StrEnum(str, Enum):
pass
Copy link

Copilot AI Sep 9, 2025

Choose a reason for hiding this comment

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

Use a more specific exception type instead of the broad Exception. Consider using ImportError since this is specifically handling a missing import scenario.

Copilot uses AI. Check for mistakes.
Comment on lines 8 to 12
try:
from dotenv import load_dotenv
except Exception: # fallback if python-dotenv isn't available
def load_dotenv(*args, **kwargs): # type: ignore[no-redef]
return False
Copy link

Copilot AI Sep 9, 2025

Choose a reason for hiding this comment

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

Use ImportError instead of the broad Exception for handling missing import of python-dotenv.

Copilot uses AI. Check for mistakes.
Comment on lines +92 to +98
try:
body = await response.json()
except Exception:
try:
body = await response.text()
except Exception:
body = None
Copy link

Copilot AI Sep 9, 2025

Choose a reason for hiding this comment

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

Use more specific exception types instead of broad Exception. For JSON parsing, consider ValueError or aiohttp.ContentTypeError. For text retrieval, consider aiohttp.ClientError.

Copilot uses AI. Check for mistakes.
Comment on lines +205 to +211
try:
body = await response.json()
except Exception:
try:
body = await response.text()
except Exception:
body = None
Copy link

Copilot AI Sep 9, 2025

Choose a reason for hiding this comment

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

Use more specific exception types instead of broad Exception. For JSON parsing, consider ValueError or aiohttp.ContentTypeError. For text retrieval, consider aiohttp.ClientError.

Copilot uses AI. Check for mistakes.
limit: Optional[int] = None,
before: Optional[str] = None,
after: Optional[str] = None,
*, request_options: dict | None = None) -> ThreadsListResponse:
Copy link

Copilot AI Sep 9, 2025

Choose a reason for hiding this comment

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

[nitpick] Consider using a typed dictionary or dataclass for request_options instead of dict | None to provide better type safety and documentation of valid options.

Copilot uses AI. Check for mistakes.
Comment on lines 11 to 35
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Setup uv
uses: astral-sh/setup-uv@v4
- name: Pin Python
run: uv python pin ${{ matrix.python-version }}
- name: Sync dependencies (dev)
run: uv sync --dev
- name: Lint (ruff)
run: uv run ruff check --fix .
- name: Type check (pyright)
run: uv run pyright .
- name: Run tests (exclude smoke)
env:
CI: "1"
run: uv run pytest -m "not smoke"
# - name: Coverage HTML (optional)
# run: uv run coverage html

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {contents: read}

Copilot Autofix

AI 3 months ago

The best way to fix this problem is to explicitly add a permissions block to the job (or at the workflow root if preferred) specifying the minimal required access: contents: read. This restricts the GITHUB_TOKEN to only permit reading repository contents, adhering to the principle of least privilege. Specifically, you should add the permissions: key under the appropriate job (here, under the build job at line 11), with the value contents: read. No extra imports, method, or definition is needed for this YAML configuration fix: it is purely a matter of amending the workflow.

Suggested changeset 1
.github/workflows/ci.yml

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -8,6 +8,8 @@
 
 jobs:
   build:
+    permissions:
+      contents: read
     runs-on: ubuntu-latest
     strategy:
       matrix:
EOF
@@ -8,6 +8,8 @@

jobs:
build:
permissions:
contents: read
runs-on: ubuntu-latest
strategy:
matrix:
Copilot is powered by AI and may make mistakes. Always verify output.
- Replace `--dev` flag with `--extra dev` in CI workflows
- Standardize string quotes to double quotes in YAML files
- Update step names to reflect new dependency sync approach
Replace all instances of `dict | None` with `RequestOptions | None` for
request_options parameters across the codebase. This change introduces
type safety and better documentation for request options.

Changes include:
- Import RequestOptions type in all relevant modules
- Update all method signatures to use RequestOptions instead of dict
- Remove unused imports and constants that are no longer needed
- Fix iterator methods to return proper typed iterators instead of generic AsyncIterator
- Update async iterator implementation to use proper async iteration protocol

BREAKING CHANGE: request_options parameter type changed from dict to RequestOptions. Users must now pass RequestOptions objects instead of plain dictionaries.
@marclove marclove changed the title V0.3.0 refactors v0.3.0 refactors Sep 10, 2025
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.

2 participants