Skip to content

Remove undocumented SharedCentralManager, fixes for dialog and related ui, remove unused overloads#11

Merged
hbmartin merged 4 commits into
masterfrom
hm/ruff-formatting-other-fixes
Aug 12, 2025
Merged

Remove undocumented SharedCentralManager, fixes for dialog and related ui, remove unused overloads#11
hbmartin merged 4 commits into
masterfrom
hm/ruff-formatting-other-fixes

Conversation

@hbmartin
Copy link
Copy Markdown
Owner

@hbmartin hbmartin commented Aug 12, 2025

Summary by Sourcery

Modernize and clean up Pythonista type stub files across multiple modules by migrating to Python 3.10+ union types and TypeAlias, standardizing list/dict syntax, removing undocumented and unused APIs, fixing UI and dialog annotations, adding best-practices documentation, and adjusting the CI workflow.

New Features:

  • Add missing UI constants (e.g., AUTOCAPITALIZE_*, HUD icons) and complete fields definitions for Objective-C structures

Enhancements:

  • Refactor all stub files to use modern union syntax, TypeAlias, and built-in collections types for consistency
  • Standardize and reorder imports and all exports, and correct parameter and return types across console, editor, dialogs, contacts, reminders, notification, sound, keyboard, canvas, speech, clipboard, shortcuts, motion, objc_util, and linguistictagger modules

CI:

  • Update CI workflow to run Black with --diff flag

Documentation:

  • Add CLAUDE.md with best practices for writing Python type stub libraries

Chores:

  • Remove undocumented SharedCentralManager and unused overloads in appex, dialogs, and photos stubs

Summary by CodeRabbit

  • New Features

    • Expanded platform APIs: richer keyboard, sound, notifications, contacts, photos, Bluetooth, new UI autocapitalization options, and improved native-structure constructors.
  • Changes

    • Modernized type hints and signatures across stubs (PEP 585/604 style), several APIs switched to overload-only forms, some functions/types added or re-exported.
  • Documentation

    • Added comprehensive guidelines for creating Python type stubs; updated README with install/docs/troubleshooting.
  • Chores

    • CI now shows Black formatting diffs; added a local AI settings file.

Important

Modernize Pythonista type stubs by using Python 3.10+ features, removing unused elements, and updating CI and documentation.

  • Behavior:
    • Remove SharedCentralManager and unused overloads in appex.pyi, dialogs.pyi, and photos.pyi.
    • Add missing UI constants and complete __fields__ definitions for Objective-C structures in ui.pyi.
  • Enhancements:
    • Refactor stubs to use modern union syntax, TypeAlias, and built-in collections types across multiple modules.
    • Standardize and reorder imports and __all__ exports in various modules.
  • CI:
    • Update CI workflow to run Black with --diff flag in ci.yml.
  • Documentation:
    • Add CLAUDE.md with best practices for writing Python type stub libraries.

This description was created by Ellipsis for 07cede9. You can customize this summary. It will automatically update as commits are pushed.

@hbmartin hbmartin requested a review from o-murphy August 12, 2025 14:10
@sourcery-ai
Copy link
Copy Markdown

sourcery-ai Bot commented Aug 12, 2025

Reviewer's Guide

This PR removes the undocumented SharedCentralManager API, modernizes type annotations to Python 3.9+ syntax across the stub files, cleans up dialog/UI stubs, prunes unused overloads, and updates CI checks and documentation.

File-Level Changes

Change Details Files
Modernize type annotations across all stub files
  • Replace Optional[T] with T
None and Union types with X
Remove undocumented SharedCentralManager from CoreBluetooth stubs
  • Eliminate SharedCentralManager class and shared_manager variable
  • Drop related delegate and verbose overloads from pythonista.cb stubs
stubs/pythonista_stubs/cb.pyi
Fix dialog and UI stub definitions
  • Add missing AUTOCAPITALIZE_* constants and correct method annotations in ui.pyi
  • Remove unused TextField stub and simplify StrConvertible protocol in dialogs.pyi
stubs/pythonista_stubs/ui.pyi
stubs/pythonista_stubs/dialogs.pyi
Prune unused overloads in appearence and share extensions
  • Remove obsolete get_images/get_image implementations in appex.pyi
  • Drop redundant pick_asset overload in photos.pyi
stubs/pythonista_stubs/appex.pyi
stubs/pythonista_stubs/photos.pyi
Update CI workflow and add documentation
  • Adjust black check to include --diff in .github/workflows/ci.yml
  • Add CLAUDE.md with stub library best practices
  • Include .claude/settings.local.json for local configuration
.github/workflows/ci.yml
CLAUDE.md
.claude/settings.local.json

Possibly linked issues

  • #0: The PR updates various stub files to improve mypy compatibility by modernizing type hints and removing undocumented APIs, directly addressing the issue.

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@openhands-ai
Copy link
Copy Markdown

openhands-ai Bot commented Aug 12, 2025

Looks like there are a few issues preventing this PR from being merged!

  • GitHub Actions are failing:
    • CI

If you'd like me to help, just leave a comment, like

@OpenHands please fix the failing actions on PR #11

Feel free to include any additional details that might help me get this PR into a better state.

You can manage your notification settings

@qodo-code-review
Copy link
Copy Markdown

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 2 🔵🔵⚪⚪⚪
🧪 No relevant tests
🔒 No security concerns identified
⚡ Recommended focus areas for review

API Accuracy

Hard-coding ctypes fields layouts for CoreGraphics/UIKit structs changes previous placeholders; verify field order, types, and sizes exactly match Pythonista’s objc_util runtime to avoid type mismatches in consumers.

    _fields_ = [("x", ctypes.c_double), ("y", ctypes.c_double)]
    x: float
    y: float
    def __init__(self, x: float = 0.0, y: float = 0.0) -> None: ...

class CGSize(ctypes.Structure):
    """Core Graphics size structure."""

    _fields_ = [("width", ctypes.c_double), ("height", ctypes.c_double)]
    width: float
    height: float
    def __init__(self, width: float = 0.0, height: float = 0.0) -> None: ...

class CGVector(ctypes.Structure):
    """Core Graphics vector structure."""

    _fields_ = [("dx", ctypes.c_double), ("dy", ctypes.c_double)]
    dx: float
    dy: float
    def __init__(self, dx: float = 0.0, dy: float = 0.0) -> None: ...

class CGRect(ctypes.Structure):
    """Core Graphics rectangle structure."""

    _fields_ = [("origin", CGPoint), ("size", CGSize)]
    origin: CGPoint
    size: CGSize
    def __init__(
        self,
        origin: CGPoint | None = None,
        size: CGSize | None = None,
    ) -> None: ...

class CGAffineTransform(ctypes.Structure):
    """Core Graphics affine transformation matrix."""

    _fields_ = [("a", ctypes.c_double), ("b", ctypes.c_double), ("c", ctypes.c_double), ("d", ctypes.c_double), ("tx", ctypes.c_double), ("ty", ctypes.c_double)]
    a: float
    b: float
    c: float
    d: float
    tx: float
    ty: float
    def __init__(
        self,
        a: float = 1.0,
        b: float = 0.0,
        c: float = 0.0,
        d: float = 1.0,
        tx: float = 0.0,
        ty: float = 0.0,
    ) -> None: ...

class UIEdgeInsets(ctypes.Structure):
    """UIKit edge insets structure."""

    _fields_ = [("top", ctypes.c_double), ("left", ctypes.c_double), ("bottom", ctypes.c_double), ("right", ctypes.c_double)]
    top: float
    left: float
    bottom: float
    right: float
    def __init__(
        self,
        top: float = 0.0,
        left: float = 0.0,
        bottom: float = 0.0,
        right: float = 0.0,
    ) -> None: ...

class NSRange(ctypes.Structure):
    """Foundation range structure."""

    _fields_ = [("location", ctypes.c_int), ("length", ctypes.c_int)]
    location: int
    length: int
Completeness

Public all expanded and several attributes now required (e.g., CH_PROP_* constants, Service.services lists). Confirm these names and signatures exist in the built-in _cb module across supported Pythonista versions.

__all__ = (
    "CH_PROP_AUTHENTICATED_SIGNED_WRITES",
    "CH_PROP_BROADCAST",
    "CH_PROP_EXTENDED_PROPERTIES",
    "CH_PROP_INDICATE",
    "CH_PROP_INDICATE_ENCRYPTION_REQUIRED",
    "CH_PROP_NOTIFY",
    "CH_PROP_NOTIFY_ENCRYPTION_REQUIRED",
    "CH_PROP_READ",
    "CH_PROP_WRITE",
    "CH_PROP_WRITE_WITHOUT_RESPONSE",
    "CM_STATE_POWERED_OFF",
    "CM_STATE_POWERED_ON",
    "CM_STATE_RESETTING",
    "CM_STATE_UNAUTHORIZED",
    "CM_STATE_UNKNOWN",
    "CM_STATE_UNSUPPORTED",
    "CentralManager",
    "Characteristic",
    "Peripheral",
    "Service",
)

CM_STATE_UNKNOWN: int = 0
CM_STATE_RESETTING: int = 1
CM_STATE_UNSUPPORTED: int = 2
CM_STATE_UNAUTHORIZED: int = 3
CM_STATE_POWERED_OFF: int = 4
CM_STATE_POWERED_ON: int = 5

CH_PROP_BROADCAST: int = 1
CH_PROP_READ: int = 2
CH_PROP_WRITE_WITHOUT_RESPONSE: int = 4
CH_PROP_WRITE: int = 8
CH_PROP_NOTIFY: int = 16
CH_PROP_INDICATE: int = 32
CH_PROP_AUTHENTICATED_SIGNED_WRITES: int = 64
CH_PROP_EXTENDED_PROPERTIES: int = 128
CH_PROP_NOTIFY_ENCRYPTION_REQUIRED: int = 256
CH_PROP_INDICATE_ENCRYPTION_REQUIRED: int = 512

class Characteristic:
    properties: int
    value: bytes | None
    uuid: str  # hex
    notifying: bool

class Service:
    characteristics: list[Characteristic]
    primary: bool
    uuid: str  # hex

class Peripheral:
    manufacturer_data: bytes
    name: str | None
    uuid: str  # hex
    state: int
    services: list[Service]

    def discover_services(self) -> None: ...
    def discover_characteristics(self, service: Service) -> None: ...
    def set_notify_value(
        self,
        characteristic: Characteristic,
        flag: bool = True,
    ) -> None: ...
    def write_characteristic_value(
        self,
        characteristic: Characteristic,
        data: bytes,
        with_response: bool,
    ) -> None: ...
    def read_characteristic_value(self, characteristic: Characteristic) -> None: ...
Breaking Change

Removal of SharedCentralManager and shared_manager alters the public API; ensure no dependent code or docs reference these and that guidance for using set_central_delegate/scan/connect is provided.

"""`pythonista.cb` module type annotations
according to [pythonista.cb docs](https://omz-software.com/pythonista/docs/ios/cb.html)
"""

from typing import Protocol

from _cb import (  # type: ignore
    CH_PROP_AUTHENTICATED_SIGNED_WRITES,
    CH_PROP_BROADCAST,
    CH_PROP_EXTENDED_PROPERTIES,
    CH_PROP_INDICATE,
    CH_PROP_INDICATE_ENCRYPTION_REQUIRED,
    CH_PROP_NOTIFY,
    CH_PROP_NOTIFY_ENCRYPTION_REQUIRED,
    CH_PROP_READ,
    CH_PROP_WRITE,
    CH_PROP_WRITE_WITHOUT_RESPONSE,
    CM_STATE_POWERED_OFF,
    CM_STATE_POWERED_ON,
    CM_STATE_RESETTING,
    CM_STATE_UNAUTHORIZED,
    CM_STATE_UNKNOWN,
    CM_STATE_UNSUPPORTED,
    CentralManager,
    Characteristic,
    Peripheral,
    Service,
)

__all__ = (
    "CH_PROP_AUTHENTICATED_SIGNED_WRITES",
    "CH_PROP_BROADCAST",
    "CH_PROP_EXTENDED_PROPERTIES",
    "CH_PROP_INDICATE",
    "CH_PROP_INDICATE_ENCRYPTION_REQUIRED",
    "CH_PROP_NOTIFY",
    "CH_PROP_NOTIFY_ENCRYPTION_REQUIRED",
    "CH_PROP_READ",
    "CH_PROP_WRITE",
    "CH_PROP_WRITE_WITHOUT_RESPONSE",
    "CM_STATE_POWERED_OFF",
    "CM_STATE_POWERED_ON",
    "CM_STATE_RESETTING",
    "CM_STATE_UNAUTHORIZED",
    "CM_STATE_UNKNOWN",
    "CM_STATE_UNSUPPORTED",
    "CentralManager",
    "Characteristic",
    "Peripheral",
    "Service",
    "cancel_peripheral_connection",
    "connect_peripheral",
    "get_state",
    "reset",
    "scan_for_peripherals",
    "set_central_delegate",
    "set_verbose",
    "stop_scan",
)

class _CentralManagerDelegate(Protocol):
    def did_discover_peripheral(self, p: Peripheral) -> None: ...
    def did_connect_peripheral(self, p: Peripheral) -> None: ...
    def did_fail_to_connect_peripheral(
        self,
        p: Peripheral,
        error: str | None,
    ) -> None: ...
    def did_disconnect_peripheral(
        self,
        p: Peripheral,
        error: str | None,
    ) -> None: ...
    def did_discover_services(self, p: Peripheral, error: str | None) -> None: ...
    def did_discover_characteristics(
        self,
        s: Service,
        error: str | None,
    ) -> None: ...
    def did_write_value(self, c: Characteristic, error: str | None) -> None: ...
    def did_update_value(self, c: Characteristic, error: str | None) -> None: ...
    def did_update_state(self) -> None: ...

def set_central_delegate(delegate: _CentralManagerDelegate) -> None: ...
def set_verbose(flag: bool) -> None: ...
def scan_for_peripherals() -> None: ...

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Summary of Changes

Hello @hbmartin, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request primarily focuses on modernizing and cleaning up the Pythonista type stub libraries. Key changes include updating type hint syntax to contemporary Python standards, removing undocumented and unused code components like SharedCentralManager, and refining existing stub definitions for various modules. These updates aim to improve the accuracy and readability of type information, enhance static analysis capabilities, and streamline the codebase by eliminating unnecessary elements, particularly within UI and dialog-related stubs.

Highlights

  • Type Hint Modernization: The majority of changes involve updating Python type hints from older typing module syntax (e.g., Optional[T], List[T], Union[T, U], Tuple[...], Dict[...]) to the more modern and concise T | None, list[T], T | U, tuple[...], and dict[...] syntax. This also includes the adoption of TypeAlias for complex type definitions and using collections.abc for abstract base classes where appropriate.
  • Removal of Undocumented/Unused Components: The undocumented SharedCentralManager class and its associated instance have been completely removed from the _cb.pyi and cb.pyi stub files, streamlining the API and removing unused or unclear components.
  • Cleanup of Redundant Overloads: Redundant or overly detailed function overloads in appex.pyi and photos.pyi have been removed, simplifying the stub files while maintaining accurate type information through proper @overload usage.
  • UI and Dialog Stub Refinements: Improvements were made to stubs related to UI and dialogs, including the removal of an unused TextField class in dialogs.pyi, the addition of autocapitalization constants in ui.pyi, and general refinement of parameter types and docstrings in console.pyi and editor.pyi.
  • Enhanced objc_util Stubs: The objc_util.pyi stub file has been enhanced by explicitly defining the _fields_ for various ctypes.Structure classes (e.g., CGPoint, CGRect), providing more complete and accurate type information for these low-level Objective-C utility structures.
  • Docstring and Formatting Improvements: Across numerous stub files, docstring formatting has been standardized, including consistent use of single quotes for specific terms and improved line breaks for readability.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in issue comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

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

Hey @hbmartin - I've reviewed your changes - here's some feedback:

  • Add a py.typed marker file to the root of the stubs package so that type checkers recognize pythonista_stubs as a typed library.
  • Dropping SharedCentralManager and related undocumented APIs may break existing scripts—please confirm this removal won’t impact users relying on that behavior or provide a deprecation path.
  • The diff converts all Optional[T] and Union[...] types to PEP 604 union syntax (T | None); please verify that Pythonista’s runtime/mypy setup supports this syntax or provide a fallback for older versions.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- Add a `py.typed` marker file to the root of the stubs package so that type checkers recognize `pythonista_stubs` as a typed library.
- Dropping `SharedCentralManager` and related undocumented APIs may break existing scripts—please confirm this removal won’t impact users relying on that behavior or provide a deprecation path.
- The diff converts all `Optional[T]` and `Union[...]` types to PEP 604 union syntax (`T | None`); please verify that Pythonista’s runtime/mypy setup supports this syntax or provide a fallback for older versions.

## Individual Comments

### Comment 1
<location> `stubs/pythonista_stubs/appex.pyi:61` </location>
<code_context>
 def get_images(image_type: Literal["pil"]) -> list[PILImage]: ...
 @overload
 def get_images(image_type: Literal["ui"]) -> list[UIImage]: ...
-def get_images(image_type: _ImageType = "pil") -> list[UIImage | PILImage]:
-    """Return a list of images in the input of the share sheet.
-
-    Args:
-        image_type (Literal['ui', 'pil'], optional): The desired image type.
-            Defaults to 'pil'.
-
-    Returns:
-        List[Union[ui.Image, PIL.Image.Image]]: A list of images.
-
-    """
-    ...
-
 @overload
</code_context>

<issue_to_address>
The main get_images overload was removed, which may reduce discoverability.

Retaining a concrete implementation alongside overloads can improve clarity for users, especially regarding default arguments. Consider adding back the main function unless stricter typing is required.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread stubs/pythonista_stubs/appex.pyi
Copy link
Copy Markdown

@ellipsis-dev ellipsis-dev Bot left a comment

Choose a reason for hiding this comment

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

Important

Looks good to me! 👍

Reviewed everything up to 5bcb39c in 1 minute and 27 seconds. Click for details.
  • Reviewed 2137 lines of code in 24 files
  • Skipped 0 files when reviewing.
  • Skipped posting 5 draft comments. View those below.
  • Modify your settings and rules to customize what types of comments Ellipsis leaves. And don't forget to react with 👍 or 👎 to teach Ellipsis.
1. stubs/pythonista_stubs/cb.pyi:47
  • Draft comment:
    Removed the undocumented SharedCentralManager and related symbols. Ensure that all dependent modules have been updated to no longer reference these deprecated APIs.
  • Reason this comment was not posted:
    Comment did not seem useful. Confidence is useful = 0% <= threshold 50% The comment is asking the PR author to ensure that all dependent modules have been updated, which violates the rule against asking the author to ensure things are tested or verified. It doesn't provide a specific suggestion or point out a specific issue with the code.
2. .github/workflows/ci.yml:29
  • Draft comment:
    The Black code formatting check now includes the --diff flag; this improves feedback on style issues.
  • Reason this comment was not posted:
    Comment did not seem useful. Confidence is useful = 0% <= threshold 50% This comment is purely informative, explaining a change in the code formatting check. It does not provide a suggestion, ask for confirmation, or highlight a potential issue. According to the rules, purely informative comments should be removed.
3. stubs/pythonista_stubs/editor.pyi:10
  • Draft comment:
    Editor stubs now consistently use the union syntax (e.g. 'str | None') for optional types, which improves clarity and modernizes the type hints.
  • Reason this comment was not posted:
    Comment did not seem useful. Confidence is useful = 0% <= threshold 50% This comment is purely informative, explaining a change that was made without suggesting any action or asking for clarification. It doesn't provide any actionable feedback or raise any concerns about the code.
4. stubs/pythonista_stubs/dialogs.pyi:67
  • Draft comment:
    Dialog-related stubs now use consistent type hints and protocols. Verify that removal of any unused overloads (if any) does not break type checking in client code.
  • Reason this comment was not posted:
    Comment did not seem useful. Confidence is useful = 0% <= threshold 50% The comment is asking the PR author to verify that the removal of unused overloads does not break type checking in client code. This falls under asking the author to ensure behavior is intended or to double-check things, which is against the rules.
5. stubs/pythonista_stubs/ui.pyi:10
  • Draft comment:
    UI stubs maintain clear signatures and updated rendering mode types. Overall, the use of PEP 604 union types (e.g., X | None) is consistent and improves readability.
  • Reason this comment was not posted:
    Comment did not seem useful. Confidence is useful = 0% <= threshold 50% This comment is purely informative, as it does not provide any actionable feedback or suggestions for improvement. It simply states that the use of PEP 604 union types is consistent and improves readability, which does not align with the rules for useful comments.

Workflow ID: wflow_gnudyT1ypImoQjMo

You can customize Ellipsis by changing your verbosity settings, reacting with 👍 or 👎, replying to comments, or adding code review rules.

@qodo-code-review
Copy link
Copy Markdown

qodo-code-review Bot commented Aug 12, 2025

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
Possible issue
Fix NSRange field sizes

Use platform-appropriate field widths for Foundation structs to avoid ABI
mismatches. On Apple platforms, NSRange uses NSUInteger which is pointer-sized;
mapping it to ctypes.c_int can truncate values on 64-bit. Switch to
ctypes.c_ulong to match NSUInteger size on iOS.

stubs/pythonista_stubs/objc_util.pyi [306-312]

 class NSRange(ctypes.Structure):
     """Foundation range structure."""
 
-    _fields_ = [("location", ctypes.c_int), ("length", ctypes.c_int)]
+    _fields_ = [("location", ctypes.c_ulong), ("length", ctypes.c_ulong)]
     location: int
     length: int
     def __init__(self, location: int = 0, length: int = 0) -> None: ...

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 9

__

Why: This suggestion correctly identifies a potential ABI mismatch for NSRange on 64-bit platforms by proposing to use ctypes.c_ulong instead of ctypes.c_int, which is a critical fix for correctness.

High
General
Narrow the type ignore scope
Suggestion Impact:The commit updated the import comment to use a specific ignore code [import-not-found], aligning with the suggestion to narrow the type ignore scope.

code diff:

-from _cb import (  # type: ignore
+from _cb import (  # type: ignore[import-not-found]

Removing the local suppression and reordering constants is fine, but using a
blanket type: ignore on the import may hide real inconsistencies between _cb.pyi
and this stub. Narrow the ignore to only missing-import to preserve other
diagnostics, ensuring tools still catch mismatched symbols.

stubs/pythonista_stubs/cb.pyi [7-28]

-from _cb import (  # type: ignore
+from _cb import (  # type: ignore[import-not-found]
     CH_PROP_AUTHENTICATED_SIGNED_WRITES,
     CH_PROP_BROADCAST,
     CH_PROP_EXTENDED_PROPERTIES,
     CH_PROP_INDICATE,
     CH_PROP_INDICATE_ENCRYPTION_REQUIRED,
     CH_PROP_NOTIFY,
     CH_PROP_NOTIFY_ENCRYPTION_REQUIRED,
     CH_PROP_READ,
     CH_PROP_WRITE,
     CH_PROP_WRITE_WITHOUT_RESPONSE,
     CM_STATE_POWERED_OFF,
     CM_STATE_POWERED_ON,
     CM_STATE_RESETTING,
     CM_STATE_UNAUTHORIZED,
     CM_STATE_UNKNOWN,
     CM_STATE_UNSUPPORTED,
     CentralManager,
     Characteristic,
     Peripheral,
     Service,
 )

[Suggestion processed]

Suggestion importance[1-10]: 6

__

Why: This is a good suggestion that improves code quality by replacing a broad # type: ignore with a more specific # type: ignore[import-not-found], which allows the type checker to catch other potential errors.

Low
Remove quoted forward references
Suggestion Impact:The commit removed quotes around the Image return type in two classmethods (from_data and named), aligning with the suggestion.

code diff:

     @classmethod
-    def from_data(cls, image_data: bytes, scale: float | None = None) -> "Image":
+    def from_data(cls, image_data: bytes, scale: float | None = None) -> Image:
         """Create an image from binary image data.
 
         Args:
@@ -25,7 +25,7 @@
         ...
 
     @classmethod
-    def named(cls, image_name: str) -> "Image" | None:
+    def named(cls, image_name: str) -> Image | None:
         """Create an image from a built-in or local image file.

Forward references like "Image" in annotations can be resolved incorrectly by
some type checkers in .pyi files. In stubs, prefer unquoted class names for
return annotations since they are evaluated as types, not runtime expressions.
Remove quotes around Image in classmethods to ensure correct stub parsing.

stubs/pythonista_stubs/ui.pyi [10-136]

 class Image:
     """Represents an image that can be displayed in the user interface."""
-    
+
     @classmethod
-    def from_data(cls, image_data: bytes, scale: float | None = None) -> "Image":
-        """Create an image from binary image data.
-        ...
-        """
-        ...
-    
+    def from_data(cls, image_data: bytes, scale: float | None = None) -> Image: ...
+
     @classmethod
-    def named(cls, image_name: str) -> "Image" | None:
-        """Create an image from a built-in or local image file.
-        ...
-        """
-        ...
-    
+    def named(cls, image_name: str) -> Image | None: ...
+
     @property
-    def scale(self) -> float:
-        """The scale factor of the image (readonly)."""
-        ...
-    
+    def scale(self) -> float: ...
+
     @property
-    def size(self) -> tuple[float, float]:
-        """The dimensions of the image as (width, height) tuple (readonly)."""
-        ...
-    
-    def clip_to_mask(self, x: float, y: float, width: float, height: float) -> None:
-        """Use this image as a mask for subsequent drawing operations.
-        ...
-        """
-        ...
-    
+    def size(self) -> tuple[float, float]: ...
+
+    def clip_to_mask(self, x: float, y: float, width: float, height: float) -> None: ...
+
     def draw(
         self,
         x: float | None = None,
         y: float | None = None,
         width: float | None = None,
         height: float | None = None,
-    ) -> None:
-        """Draw the image in the current drawing context.
-        ...
-        """
-        ...
-    
-    def draw_as_pattern(self, x: float, y: float, width: float, height: float) -> None:
-        """Fill a rectangle with this image as a repeating pattern.
-        ...
-        """
-        ...
-    
-    def resizable_image(
-        self,
-        top: float,
-        left: float,
-        bottom: float,
-        right: float,
-    ) -> Image:
-        """Create a 9-patch resizable version of this image.
-        ...
-        """
-        ...
-    
-    def show(self) -> None:
-        """Display the image in the console output."""
-        ...
-    
-    def to_png(self) -> bytes:
-        """Convert the image to PNG format.
-        ...
-        """
-        ...
-    
-    def with_rendering_mode(self, mode: _RenderingMode) -> Image:
-        """Create a new image with the specified rendering mode.
-        ...
-        """
-        ...
+    ) -> None: ...
 
+    def draw_as_pattern(self, x: float, y: float, width: float, height: float) -> None: ...
+
+    def resizable_image(self, top: float, left: float, bottom: float, right: float) -> Image: ...
+
+    def show(self) -> None: ...
+
+    def to_png(self) -> bytes: ...
+
+    def with_rendering_mode(self, mode: _RenderingMode) -> Image: ...
+

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 5

__

Why: The suggestion correctly points out that using quoted forward references for Image within its own class in a .pyi file is unnecessary and against best practices, improving stub quality and compatibility with type checkers.

Low
  • Update

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request significantly modernizes the type annotations across numerous stub files, which is a great enhancement for type checking and developer experience. The removal of the undocumented SharedCentralManager and other unused code is a positive step towards cleaning up the API. The CI and documentation updates are also valuable additions. I have identified a few critical issues where removing the implementation signature for @overload functions will cause errors with type checkers. Additionally, there's a minor issue with a protocol definition that has been weakened. Addressing these points will ensure the stub files are robust and correct.

Comment thread stubs/pythonista_stubs/appex.pyi
Comment thread stubs/pythonista_stubs/dialogs.pyi Outdated
Comment thread CLAUDE.md Outdated
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Aug 12, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Caution

Review failed

The pull request is closed.

Walkthrough

Adds a local Claude permissions file, adjusts CI Black check to show diffs, introduces CLAUDE.md, and modernizes many Pythonista stub typings (PEP 585/604, TypeAlias). Several stubs change exported symbols (BLE re-exports and top-level functions, keyboard/sound/contacts/reminders expansions, objc_util structs, ui constants) and some overload/concrete-signature edits.

Changes

Cohort / File(s) Summary of changes
Config & CI
.claude/settings.local.json, /.github/workflows/ci.yml
New local Claude permissions file (allow WebFetch domain); CI Black check now runs with --diff . to emit formatting diffs.
Documentation
/CLAUDE.md, README.md
New CLAUDE.md with stub-authoring guidance; README links/installation/docs table and troubleshooting expanded/updated.
BLE core stubs
stubs/_cb.pyi, stubs/pythonista_stubs/cb.pyi
Typing modernization (PEP 585/604), expanded __all__, error params now `str
Wide typing modernization
stubs/pythonista_stubs/{appex.pyi,canvas.pyi,clipboard.pyi,console.pyi,contacts.pyi,editor.pyi,keychain.pyi,linguistictagger.pyi,motion.pyi,notification.pyi,reminders.pyi,shortcuts.pyi,speech.pyi}
Convert Optional/Union/List/Tuple/Dict to PEP 604 unions and built-in generics; add TypeAlias where used; many signatures updated to `X
Keyboard API updates
stubs/pythonista_stubs/keyboard.pyi
Import View from .ui; add functions (get_appearance, has_full_access, has_text, insert_text, is_keyboard, move_cursor); introduce _Appearance/_Mode as TypeAlias; update signatures to modern syntax.
Sound API updates
stubs/pythonista_stubs/sound.pyi
Typing modernized; play_effect returns `Effect
Contacts & Reminders
stubs/pythonista_stubs/contacts.pyi, stubs/pythonista_stubs/reminders.pyi
Add new Person fields and Alarm.proximity; add add_group/remove_group and delete_reminder/delete_calendar; convert many Optionals/Lists/Tuples to modern unions/built-ins.
objc_util struct fields
stubs/pythonista_stubs/objc_util.pyi
Replace placeholder _fields_ with explicit attributes for CGPoint/CGSize/CGVector/CGRect/CGAffineTransform/UIEdgeInsets/NSRange; add/adjust constructors; rename TypeVar to _F.
UI constants & image typing
stubs/pythonista_stubs/ui.pyi, stubs/pythonista_stubs/photos.pyi, stubs/pythonista_stubs/appex.pyi
Add AUTOCAPITALIZE_* constants; remove some concrete signatures (e.g., non-overload pick_asset, get_image(s) concrete); image-related size/type annotations modernized.
Dialogs & form schemas
stubs/pythonista_stubs/dialogs.pyi
Remove TextField class; introduce TypedDict-based field dicts (TextFieldDict, SwitchFieldDict, DateTimeFieldDict) and FormFieldDict union; update form-related signatures and return types.
Small normalizations & doc tweaks
stubs/pythonista_stubs/{canvas.pyi,console.pyi,keychain.pyi,linguistictagger.pyi,motion.pyi,notification.pyi,editor.pyi,clipboard.pyi,speech.pyi}
Minor signature rewrites to tuple/list/TypeAlias forms, docstring wording/formatting adjustments, and small reflows/wraps.

Sequence Diagram(s)

sequenceDiagram
  participant App as Application code
  participant cb as pythonista_stubs.cb
  participant _cb as _cb (native backend)

  App->>cb: stop_scan()
  App->>cb: connect_peripheral(p)
  cb->>_cb: CentralManager.connect_peripheral(p)
  _cb-->>cb: async did_* callbacks (error: str | None)
  cb-->>App: delegate Protocol methods invoked (did_* with str | None)
  App->>cb: cancel_peripheral_connection(p)
  App->>cb: reset()
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

Review effort 4/5

Suggested reviewers

  • o-murphy

Poem

I twitch my whiskers, tidy types at night,
I hop through stubs and set the unions right.
Tuples, lists, and TypeAlias in view,
New constants, structs, and functions too.
A rabbit's cheer — clean code takes flight! 🐇✨


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 07cede9 and a95f1c8.

📒 Files selected for processing (1)
  • README.md (3 hunks)
✨ Finishing Touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch hm/ruff-formatting-other-fixes

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Copy Markdown

@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: 4

♻️ Duplicate comments (3)
stubs/pythonista_stubs/dialogs.pyi (1)

67-67: Restore the protocol contract for StrConvertible

Making StrConvertible an empty Protocol weakens the type signal; it no longer encodes the requirement that items are string-convertible. Recommend restoring the str contract.

Apply this diff:

-class StrConvertible(Protocol): ...
+class StrConvertible(Protocol):
+    def __str__(self) -> str: ...
CLAUDE.md (1)

40-40: Fix spacing in code example.

There's an extra space after @overload that could confuse developers copying this snippet.

-@overload  
+@overload
stubs/pythonista_stubs/appex.pyi (1)

55-66: Add implementation signatures after overloads to comply with PEP 484.

The overloaded functions get_images and get_image need non-decorated implementation signatures after their overloads. Without these, type checkers like mypy will raise errors.

Add these implementation signatures after the overloads:

 @overload
 def get_images(image_type: Literal["ui"]) -> list[UIImage]: ...
+def get_images(image_type: _ImageType = "pil") -> list[UIImage | PILImage]: ...
+
 @overload
 def get_image() -> PILImage | None: ...
 @overload
 def get_image(image_type: Literal["pil"]) -> PILImage | None: ...
 @overload
 def get_image(image_type: Literal["ui"]) -> UIImage | None: ...
+def get_image(image_type: _ImageType = "pil") -> UIImage | PILImage | None: ...
🧹 Nitpick comments (21)
.claude/settings.local.json (1)

3-5: Scope the allow-list deliberately

The WebFetch allow-list is narrowly scoped to omz-software.com, which is good. If subdomains (e.g., www.omz-software.com) are required, add them explicitly rather than broadening the domain match.

stubs/pythonista_stubs/linguistictagger.pyi (1)

35-46: Update docstrings to match modernized annotations

The signature now returns list[tuple[str, str, tuple[int, int]]] and accepts scheme: _Scheme, but the docstring still refers to typing.List/Tuple and to scheme as str. Align the doc to reduce confusion for users and tools.

Proposed docstring tweaks (within the function’s docstring):

-    Args:
-        string (str): The text to be tagged.
-        scheme (str): The tagging scheme to use.
+    Args:
+        string (str): The text to be tagged.
+        scheme (_Scheme): The tagging scheme to use.

-    Returns:
-        List[Tuple[str, str, Tuple[int, int]]]: A list of (tag, substring, range)
-            tuples.
+    Returns:
+        list[tuple[str, str, tuple[int, int]]]: A list of (tag, substring, range)
+            tuples.
stubs/pythonista_stubs/notification.pyi (1)

26-43: Update docstring to reflect modern type syntax.

The docstring still references old-style type hints (e.g., List[str], List[dict]) while the actual signature uses modern syntax. This inconsistency could confuse users.

Apply this diff to update the docstring:

     Args:
         message (str, optional): The main text of the notification.
         delay (float, optional): The time in seconds until delivery. Defaults to 0.
         sound_name (str, optional): The sound to play. Use 'default' for the
             default sound or None for silent.
         action_url (str, optional): The URL to launch when the notification is tapped.
         title (str, optional): The title of the notification.
         subtitle (str, optional): The subtitle of the notification.
-        attachments (List[str], optional): A list of file paths to attach.
+        attachments (list[str], optional): A list of file paths to attach.
         trigger (dict, optional): A dictionary for more complex triggers.
-        actions (List[dict], optional): Definitions for custom action buttons.
+        actions (list[dict], optional): Definitions for custom action buttons.
         identifier (str, optional): An optional identifier for the notification.

     Returns:
         str: The identifier of the scheduled notification.
stubs/pythonista_stubs/reminders.pyi (2)

72-87: Update docstring to use modern type syntax.

Similar to the notification.pyi file, the docstring contains outdated type references that don't match the modernized signature.

Apply this diff to update the docstring:

     Args:
         calendar (Calendar, optional): The calendar to get reminders from.
             Defaults to None.
         completed (bool, optional): Filters reminders by completion status.
             Defaults to None (all reminders).

     Returns:
-        List[Reminder]: A list of Reminder objects.
+        list[Reminder]: A list of Reminder objects.

98-107: Update docstring to use modern type syntax.

The docstring should be updated to reflect the modern return type syntax.

Apply this diff:

     Returns:
-        Optional[Calendar]: The Calendar object, or None if not found.
+        Calendar | None: The Calendar object, or None if not found.
stubs/pythonista_stubs/sound.pyi (1)

10-30: Update docstring to use modern type syntax.

The docstring return type should match the modernized function signature.

Apply this diff:

     Returns:
-        Optional[Effect]: An Effect object, or None if too many effects are playing.
+        Effect | None: An Effect object, or None if too many effects are playing.
stubs/pythonista_stubs/editor.pyi (9)

10-17: Update docstring to use modern type syntax.

The docstring should be updated to match the modernized return type.

Apply this diff:

     Returns:
-        Optional[str]: The absolute file path, or None if no script is open.
+        str | None: The absolute file path, or None if no script is open.

28-37: Update docstring to use modern type syntax.

The docstring should use the modern union and tuple syntax.

Apply this diff:

     Returns:
-        Optional[Tuple[int, int]]: The start and end indices of the selection,
+        tuple[int, int] | None: The start and end indices of the selection,
             or None if no file is currently open.

40-47: Update docstring to use modern type syntax.

Similar to the previous comment, update the return type documentation.

Apply this diff:

     Returns:
-        Optional[Tuple[int, int]]: The start and end indices of the line selection,
+        tuple[int, int] | None: The start and end indices of the line selection,
             or None if no file is currently open.

50-60: Update docstring to use modern type syntax.

The docstring parameter type should match the modernized signature.

Apply this diff:

     Args:
         start (int): The starting character index of the selection.
-        end (Optional[int], optional): The ending character index of the selection.
+        end (int | None, optional): The ending character index of the selection.
             If None, the caret is positioned at `start` with no text selected.
         scroll (bool, optional): If True, scrolls the view to make the selection
             visible. Defaults to False.

77-88: Update docstring to use modern type syntax.

The docstring should reflect the modernized parameter types.

Apply this diff:

     Args:
-        name (Optional[str], optional): The desired name for the new file.
+        name (str | None, optional): The desired name for the new file.
             Defaults to None.
-        content (Optional[str], optional): The initial content of the new file.
+        content (str | None, optional): The initial content of the new file.
             If omitted, an empty file is created. Defaults to None.

105-112: Update docstring to use modern type syntax.

Update the parameter type in the docstring.

Apply this diff:

     Args:
         ui_view (ui.View): The view to be styled.
-        theme_name (Optional[str], optional): The name of the theme. If None,
+        theme_name (str | None, optional): The name of the theme. If None,
             the currently selected theme is used. Defaults to None.

116-131: Update docstring to use modern type syntax.

The docstring should reflect the modernized parameter type.

Apply this diff:

     Args:
         ui_view (ui.View): The view to be styled and presented.
-        theme_name (Optional[str], optional): The name of the theme. If None,
+        theme_name (str | None, optional): The name of the theme. If None,
             the currently selected theme is used. Defaults to None.

136-159: Update docstring to use modern type syntax.

The filename parameter type should be updated in the docstring.

Apply this diff:

         expanded (bool, optional): If False, only an icon is shown; tapping
             shows the text. Defaults to True.
-        filename (Optional[str], optional): The path to the file to annotate.
+        filename (str | None, optional): The path to the file to annotate.
             If None, the file currently open in the editor is used.
             Defaults to None.
         scroll (bool, optional): If True, scrolls to the annotated line.
             Defaults to False.

162-169: Update docstring to use modern type syntax.

The filename parameter type should be updated.

Apply this diff:

     Args:
-        filename (Optional[str], optional): The path to the file from which to
+        filename (str | None, optional): The path to the file from which to
             clear annotations. If None, the file currently open is used.
             Defaults to None.
stubs/pythonista_stubs/objc_util.pyi (2)

273-273: Consider line length for readability.

The fields definition spans 273 characters on a single line, which significantly exceeds typical Python style guidelines (79-120 characters). While this is a stub file, breaking it into multiple lines would improve readability.

Apply this diff to improve readability:

-    _fields_ = [("a", ctypes.c_double), ("b", ctypes.c_double), ("c", ctypes.c_double), ("d", ctypes.c_double), ("tx", ctypes.c_double), ("ty", ctypes.c_double)]
+    _fields_ = [
+        ("a", ctypes.c_double),
+        ("b", ctypes.c_double),
+        ("c", ctypes.c_double),
+        ("d", ctypes.c_double),
+        ("tx", ctypes.c_double),
+        ("ty", ctypes.c_double),
+    ]

293-293: Consider line length for readability.

Similar to CGAffineTransform, this line is quite long and would benefit from multi-line formatting.

Apply this diff:

-    _fields_ = [("top", ctypes.c_double), ("left", ctypes.c_double), ("bottom", ctypes.c_double), ("right", ctypes.c_double)]
+    _fields_ = [
+        ("top", ctypes.c_double),
+        ("left", ctypes.c_double),
+        ("bottom", ctypes.c_double),
+        ("right", ctypes.c_double),
+    ]
stubs/pythonista_stubs/contacts.pyi (2)

117-119: Clarify docstrings to reflect optional return values

The signatures return Group | None and Person | None, but the docstrings don’t indicate the None case. Recommend aligning docstrings for clarity.

Apply this diff:

 def get_group(group_id: int) -> Group | None:
-    """Return the Group with the given id."""
+    """Return the Group with the given id, or None if not found."""
     ...
@@
 def get_person(person_id: int) -> Person | None:
-    """Return the Person with the given id."""
+    """Return the Person with the given id, or None if not found."""
     ...

Also applies to: 154-156


60-63: Model read-only attributes as properties to enforce immutability

Fields documented as read-only (e.g., full_name, id, creation_date, modification_date, vcard, and Group.id) are declared as assignable attributes. In .pyi stubs, prefer @property to prevent tools from allowing assignment.

Example adjustment (apply within the respective classes):

class Person:
    @property
    def full_name(self) -> str: ...
    @property
    def id(self) -> int: ...
    @property
    def creation_date(self) -> datetime.datetime: ...
    @property
    def modification_date(self) -> datetime.datetime: ...
    @property
    def vcard(self) -> str: ...

class Group:
    @property
    def id(self) -> int: ...

Also applies to: 100-101, 109-113

stubs/pythonista_stubs/cb.pyi (1)

5-6: Use a Literal-based alias for Central Manager state for stronger typing

get_state() currently returns int, though it can be constrained to the known CM_STATE_* constants. Tightening this improves IDE help and static checking.

Apply this diff to the imports and function signature:

-from typing import Protocol
+from typing import Protocol, Literal
@@
-def get_state() -> int: ...
+def get_state() -> CentralManagerState: ...

Add the following alias after the _cb imports:

# Place after the _cb imports
CentralManagerState = Literal[
    CM_STATE_POWERED_OFF,
    CM_STATE_POWERED_ON,
    CM_STATE_RESETTING,
    CM_STATE_UNAUTHORIZED,
    CM_STATE_UNKNOWN,
    CM_STATE_UNSUPPORTED,
]

Also applies to: 90-91

stubs/_cb.pyi (1)

32-49: Optional: introduce Literal-based aliases for state and properties

For clearer intent and better static checks, consider type aliases for CM states and CH property bit flags. This is non-blocking but improves ergonomics across the stubs.

Example:

from typing import Literal, TypeAlias

CentralManagerState: TypeAlias = Literal[
    CM_STATE_POWERED_OFF,
    CM_STATE_POWERED_ON,
    CM_STATE_RESETTING,
    CM_STATE_UNAUTHORIZED,
    CM_STATE_UNKNOWN,
    CM_STATE_UNSUPPORTED,
]

# A simple alias for bitmask properties; Literal can be too restrictive for combos
CharacteristicProperties: TypeAlias = int

Then use CentralManagerState where applicable (e.g., CentralManager.state: CentralManagerState) and keep Characteristic.properties: CharacteristicProperties.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4e5bdcc and 5bcb39c.

📒 Files selected for processing (24)
  • .claude/settings.local.json (1 hunks)
  • .github/workflows/ci.yml (1 hunks)
  • CLAUDE.md (1 hunks)
  • stubs/_cb.pyi (3 hunks)
  • stubs/pythonista_stubs/appex.pyi (11 hunks)
  • stubs/pythonista_stubs/canvas.pyi (6 hunks)
  • stubs/pythonista_stubs/cb.pyi (1 hunks)
  • stubs/pythonista_stubs/clipboard.pyi (4 hunks)
  • stubs/pythonista_stubs/console.pyi (15 hunks)
  • stubs/pythonista_stubs/contacts.pyi (5 hunks)
  • stubs/pythonista_stubs/dialogs.pyi (1 hunks)
  • stubs/pythonista_stubs/editor.pyi (8 hunks)
  • stubs/pythonista_stubs/keyboard.pyi (3 hunks)
  • stubs/pythonista_stubs/keychain.pyi (5 hunks)
  • stubs/pythonista_stubs/linguistictagger.pyi (3 hunks)
  • stubs/pythonista_stubs/motion.pyi (2 hunks)
  • stubs/pythonista_stubs/notification.pyi (2 hunks)
  • stubs/pythonista_stubs/objc_util.pyi (4 hunks)
  • stubs/pythonista_stubs/photos.pyi (0 hunks)
  • stubs/pythonista_stubs/reminders.pyi (3 hunks)
  • stubs/pythonista_stubs/shortcuts.pyi (1 hunks)
  • stubs/pythonista_stubs/sound.pyi (9 hunks)
  • stubs/pythonista_stubs/speech.pyi (2 hunks)
  • stubs/pythonista_stubs/ui.pyi (1 hunks)
💤 Files with no reviewable changes (1)
  • stubs/pythonista_stubs/photos.pyi
🧰 Additional context used
🪛 LanguageTool
CLAUDE.md

[style] ~70-~70: To elevate your writing, try using a synonym here.
Context: ...g without being so complex that they're hard to maintain or understand.

(HARD_TO)

🔇 Additional comments (55)
.claude/settings.local.json (1)

2-7: Confirm intent to commit a “local” settings file to VCS

If this file is intended for local developer overrides, committing it may cause unintended behavior in CI or other environments. Consider keeping a shared settings.json under version control and adding settings.local.json to .gitignore, or confirm that this “local” file is meant to be tracked.

.github/workflows/ci.yml (1)

29-31: LGTM: black now emits diffs without changing failure semantics

Adding --diff . improves developer feedback while keeping the check-only behavior. No further changes needed here.

stubs/pythonista_stubs/linguistictagger.pyi (1)

22-30: LGTM: Explicit TypeAlias improves clarity

Using TypeAlias for _Scheme is clear and consistent with PEP 613 usage in stubs.

stubs/pythonista_stubs/keychain.pyi (1)

13-23: LGTM: PEP 604 union for get_password

Using str | None is consistent with the repo-wide modernization and keeps behavior unchanged.

stubs/pythonista_stubs/ui.pyi (3)

5-8: LGTM! Well-documented autocapitalize constants.

Adding these autocapitalize constants provides essential type information for UI text input configuration. The constants follow the standard Pythonista naming conventions and are properly typed.


28-28: Consistent forward reference usage.

The forward reference pattern is consistently applied across both class methods, which maintains consistency within the file.


62-68: Improved method signature formatting.

The multiline parameter formatting enhances readability and follows Python formatting best practices for long parameter lists.

CLAUDE.md (1)

1-71: Comprehensive and well-structured documentation.

This documentation provides excellent guidance for Python type stub creation, covering all essential aspects from structure to testing. The examples are practical and the organization makes it easy to follow best practices.

stubs/pythonista_stubs/motion.pyi (2)

1-3: Improved docstring formatting.

The module docstring is now more concise while preserving all the essential information about the stub file's purpose.


16-32: Excellent modernization to built-in tuple types.

The conversion from Tuple[float, float, float] to tuple[float, float, float] aligns with modern Python typing practices (PEP 585) and removes the dependency on typing.Tuple. All function signatures are correctly updated and the docstring formatting improvements enhance readability.

stubs/pythonista_stubs/shortcuts.pyi (3)

5-10: Good adoption of modern typing practices.

The introduction of TypeAlias and the literal type definition follows current Python typing best practices and improves code readability.


24-25: Consistent union syntax modernization.

The conversion from Optional[str] and Optional[List[str]] to str | None and list[str] | None respectively follows PEP 604 union syntax and built-in generics (PEP 585), making the code more modern and readable.


42-42: Complete type modernization across all functions.

All function signatures have been consistently updated to use the modern union syntax, maintaining API compatibility while improving type annotation clarity.

stubs/pythonista_stubs/canvas.pyi (3)

43-45: Consistent tuple type modernization.

The conversion to built-in tuple[float, float] from Tuple[float, float] aligns with PEP 585 and removes unnecessary typing imports.


186-194: Proper union syntax for optional parameters.

The conversion from Optional[float] to float | None for the width and height parameters follows modern Python typing conventions while maintaining the same semantic meaning.


200-225: Comprehensive typing modernization.

All remaining functions consistently use the modern built-in tuple types and the parameter formatting improvements enhance readability across the entire module.

stubs/pythonista_stubs/speech.pyi (1)

1-59: LGTM! Typing modernization applied correctly.

The changes modernize the type hints to use Python 3.10+ syntax (built-in generics and PEP 604 union syntax) consistently throughout the file. All function signatures and docstrings have been properly updated.

stubs/pythonista_stubs/appex.pyi (1)

1-178: Typing modernization applied consistently.

The file properly modernizes type hints to use Python 3.10+ syntax throughout, with correct usage of TypeAlias and built-in generics.

stubs/pythonista_stubs/clipboard.pyi (3)

10-13: PIL import fallback is handled correctly.

The try/except block properly handles the case where PIL might not be available, providing a suitable fallback with Any type.


50-50: Good use of TypeAlias for the image format literal.

Using TypeAlias for the _ImageFormat literal type improves maintainability and makes the type definition more explicit.


1-69: Typing modernization successfully applied.

All type hints have been properly updated to use Python 3.10+ syntax with | unions and built-in generics consistently throughout the file.

stubs/pythonista_stubs/console.pyi (2)

185-185: Excellent use of TypeAlias for the HUD icon literal.

Using TypeAlias for _HudIcon makes the type definition more explicit and maintainable.


1-259: Comprehensive typing modernization applied correctly.

All type hints have been properly updated to use Python 3.10+ syntax with:

  • | unions instead of Optional
  • Built-in generics (tuple[str, str] instead of Tuple[str, str])
  • TypeAlias for literal types
  • Proper use of Sequence from collections.abc
stubs/pythonista_stubs/keyboard.pyi (4)

7-7: Good consolidation of View import from ui module.

Importing View from the centralized ui module instead of defining it locally improves maintainability and reduces duplication.


12-13: Proper use of TypeAlias for literal types.

Using TypeAlias for _Appearance and _Mode literal types makes the type definitions more explicit and maintainable.


24-27: New public functions enhance keyboard module functionality.

The addition of get_appearance(), has_full_access(), has_text(), insert_text(), is_keyboard(), and move_cursor() functions provides useful functionality for keyboard interaction.

Also applies to: 50-57, 58-65, 66-69


1-84: Typing modernization and API enhancements applied successfully.

The file has been properly updated with:

  • Modern Python 3.10+ type syntax throughout
  • Proper TypeAlias usage for literal types
  • Consistent use of built-in generics
  • Well-documented new public functions
stubs/pythonista_stubs/notification.pyi (4)

1-11: LGTM! Type modernization aligns with Python 3.10+ standards.

The changes correctly modernize the type hints to use built-in generics and TypeAlias, which is consistent with PEP 585 and PEP 613. The TypeAlias definitions for _Action and _Trigger appropriately capture the dictionary structures used by the notification module.


13-24: LGTM! Function signature modernization is correct.

The conversion from Optional[T] to T | None follows modern Python typing conventions. The parameter order and defaults are preserved correctly.


59-61: LGTM! Return type correctly modernized.

The change from List[str] to list[str] properly uses the built-in generic type.


72-74: LGTM! New function addition is appropriate.

The remove_all_delivered() function is a logical addition that complements the existing remove_delivered() function, following the same pattern as cancel() and cancel_all().

stubs/pythonista_stubs/reminders.pyi (4)

14-23: LGTM! Alarm class attributes properly modernized.

The type updates correctly use modern union syntax and the addition of the proximity attribute with a Literal type properly constrains the valid values.


31-31: LGTM! Constructor signature correctly updated.

The __init__ method signature properly uses the modern union syntax for the optional Calendar parameter.


33-48: LGTM! Reminder attributes properly modernized.

All attributes correctly use the modern type syntax with built-in generics and union types.


90-92: LGTM! Return type correctly modernized.

The change to list[Calendar] properly uses the built-in generic type.

stubs/pythonista_stubs/sound.pyi (7)

5-6: LGTM! Import correctly uses collections.abc.

The import properly uses collections.abc which is the recommended module for abstract base classes since Python 3.3.


36-38: LGTM! Function signature correctly updated.

The removal of forward reference quotes is appropriate since the Effect class is defined later in the same file.


56-79: LGTM! Effect class enhancements are appropriate.

The addition of the stop() method and property getters/setters provides a complete interface for the Effect class. The tuple type annotations correctly use built-in generics.


103-108: LGTM! Player class attributes properly modernized.

The finished_handler type correctly uses the modern union syntax, and the addition of the playing attribute completes the interface.


119-125: LGTM! Recorder method signature correctly updated.

The record method parameter properly uses the modern union syntax for the optional duration.


139-142: LGTM! Meters attribute type correctly modernized.

The type properly uses the built-in tuple generic instead of Tuple.


150-150: LGTM! MIDIPlayer constructor correctly updated.

The optional sound_bank_path parameter properly uses the modern union syntax.

stubs/pythonista_stubs/editor.pyi (2)

5-6: LGTM! Import statement correctly uses TypeAlias.

The addition of TypeAlias import is appropriate for the type alias definition later in the file.


134-134: LGTM! TypeAlias definition is appropriate.

The _AnnotationStyle TypeAlias correctly constrains the valid annotation styles using Literal types.

stubs/pythonista_stubs/objc_util.pyi (5)

237-237: Verify the c_double type for iOS/macOS compatibility.

The CGPoint structure uses ctypes.c_double for x and y coordinates. On iOS/macOS, CGFloat is typically defined as double on 64-bit systems but float on 32-bit systems. Since Pythonista runs on iOS devices which are all 64-bit since iOS 11, using c_double should be correct.


245-245: LGTM! CGSize fields correctly defined.

The width and height fields appropriately use c_double, consistent with CGPoint.


253-253: LGTM! CGVector fields correctly defined.

The dx and dy fields appropriately use c_double for vector components.


261-268: LGTM! CGRect structure properly composed.

The CGRect correctly uses CGPoint and CGSize as composite structures, which matches the actual Core Graphics definition.


280-288: LGTM! CGAffineTransform constructor provides sensible defaults.

The default values correctly represent an identity transform matrix (a=1, d=1, others=0).

stubs/pythonista_stubs/contacts.pyi (1)

44-49: Modernized typing (PEP 585/604) looks correct and consistent — LGTM

Use of built-in generics and | None across Person fields and function return types aligns with the PR’s objectives and improves readability. No issues spotted.

Also applies to: 54-55, 64-67, 88-99, 146-156

stubs/pythonista_stubs/cb.pyi (2)

7-28: Re-exports and expanded public surface are coherent — LGTM

Importing and exposing CentralManager, Peripheral, Service, Characteristic, and the CH_PROP_*/CM_STATE_* constants match the wrapper’s intent and align with the updated _cb stub. __all__ is consistent with the declared API.

Also applies to: 30-59


86-89: Confirmed scan_for_peripherals signature is accurate
Per the official Pythonista docs, cb.scan_for_peripherals() takes no arguments—no service UUID filters or allow_duplicates flag are exposed—so the existing stub matches the documentation.

stubs/_cb.pyi (3)

50-60: Type modernization for data holders — LGTM

  • Characteristic.value: bytes | None
  • Service.characteristics: list[Characteristic]
  • Peripheral.name: str | None
  • Peripheral.services: list[Service]

These are appropriate and consistent with the wrapper.

Also applies to: 63-67


94-111: Callback signatures unified with optional error — LGTM

The delegate/error callbacks now consistently use error: str | None, matching the wrapper and improving call-site typing.


76-80: with_response is required, no default
According to Pythonista’s documentation, Peripheral.write_characteristic_value(characteristic, data, with_response) does not provide a default for with_response; callers must pass True or False explicitly. The existing stub signature correctly reflects the runtime behavior—no default should be added.

Comment thread stubs/pythonista_stubs/dialogs.pyi Outdated
class StrConvertible(Protocol): ...

T = TypeVar('T', bound=StrConvertible)
T = TypeVar("T", bound=StrConvertible)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

list_dialog typing should reflect dict-based items and multiple-selection return shape

Given the docstring states items can be dicts and the return varies with multiple, overloads would provide precise types. Current signature (items: list[T] | None) and return (list[T] | None) don’t capture those behaviors.

Below is a concrete stub refactor using overloads (changes occur outside the selected lines):

from typing import overload

_ItemDict = dict[str, Any]

@overload
def list_dialog(
    title: str = "",
    items: Sequence[T] | None = None,
    multiple: Literal[False] = False,
) -> T | None: ...

@overload
def list_dialog(
    title: str = "",
    items: Sequence[T] | None = None,
    multiple: Literal[True] = True,
) -> list[T] | None: ...

@overload
def list_dialog(
    title: str = "",
    items: list[_ItemDict] | None = None,
    multiple: Literal[False] = False,
) -> _ItemDict | None: ...

@overload
def list_dialog(
    title: str = "",
    items: list[_ItemDict] | None = None,
    multiple: Literal[True] = True,
) -> list[_ItemDict] | None: ...

def list_dialog(
    title: str = "",
    items: Sequence[Any] | None = None,
    multiple: bool = False,
) -> Any: ...

This aligns the stub with the documented behavior while preserving strong typing for common cases.

🤖 Prompt for AI Agents
In stubs/pythonista_stubs/dialogs.pyi around line 69, the current TypeVar-based
signature for list_dialog does not capture dict-item cases nor the different
return shapes when multiple=True; replace the single generic signature with a
set of typing.overload declarations: add overloads for Sequence[T] with
multiple=False returning T | None and multiple=True returning list[T] | None;
add overloads for items as list[dict[str, Any]] with multiple=False returning
dict[str, Any] | None and multiple=True returning list[dict[str, Any]] | None;
finally keep a broad implementation signature accepting Sequence[Any] | None and
multiple: bool -> Any. Ensure to import overload and Any and define a helper
alias _ItemDict if desired.

...

def get_services() -> List[Tuple[Any, Any]]: ...
def get_services() -> list[tuple[Any, Any]]: ...
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Tighten get_services return types to str and add a brief docstring

The tuple elements are service and account identifiers, which are strings in Pythonista’s keychain API. list[tuple[str, str]] offers better precision than list[tuple[Any, Any]] and improves downstream type-checking.

Apply this diff:

-def get_services() -> list[tuple[Any, Any]]: ...
+def get_services() -> list[tuple[str, str]]:
+    """Return (service, account) pairs stored in the keychain."""
+    ...

If there are edge cases where non-str values can appear (e.g., bytes), please confirm; we can adjust to a wider type or add overloads if needed.


🌐 Web query:

What is the return type of keychain.get_services() in Pythonista's keychain module? Are service and account identifiers returned as strings?

💡 Result:

Short answer: keychain.get_services() returns a Python list of 2‑tuples (service, account). The service and account values are returned as Python strings (i.e. native str in Pythonista‑3 / Unicode text). An empty list is returned if nothing is stored.

Sources / implementations that show this behavior. (gist.github.com, blackmamba.readthedocs.io, omz-software.com)

If you want to verify on your device, run something like:

import keychain
services = keychain.get_services()
print(type(services), services) # list
if services:
print(type(services[0][0]), type(services[0][1])) # should be <class 'str'> (Pythonista‑3)

Citations:


Tighten get_services return type and add docstring

Verified that keychain.get_services() always returns a list of (service, account) pairs as native Python str. No byte or other types are emitted, so we can safely refine the stub signature.

• File: stubs/pythonista_stubs/keychain.pyi, around line 61
Apply this diff:

-def get_services() -> list[tuple[Any, Any]]: ...
+def get_services() -> list[tuple[str, str]]:
+    """Return (service, account) pairs stored in the keychain."""
+    ...
📝 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
def get_services() -> list[tuple[Any, Any]]: ...
def get_services() -> list[tuple[str, str]]:
"""Return (service, account) pairs stored in the keychain."""
...
🤖 Prompt for AI Agents
In stubs/pythonista_stubs/keychain.pyi around line 61, the get_services() stub
currently returns list[tuple[Any, Any]] but the implementation always yields a
list of (service, account) pairs as native Python str; change the signature to
return list[tuple[str, str]] and add a brief docstring above the function that
explains it returns a list of (service, account) string tuples (no bytes),
ensuring typing tools and users see the tightened return type.

Comment thread stubs/pythonista_stubs/objc_util.pyi Outdated
Comment thread stubs/pythonista_stubs/ui.pyi Outdated
Copy link
Copy Markdown

@ellipsis-dev ellipsis-dev Bot left a comment

Choose a reason for hiding this comment

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

Important

Looks good to me! 👍

Reviewed 07cede9 in 1 minute and 49 seconds. Click for details.
  • Reviewed 643 lines of code in 17 files
  • Skipped 0 files when reviewing.
  • Skipped posting 17 draft comments. View those below.
  • Modify your settings and rules to customize what types of comments Ellipsis leaves. And don't forget to react with 👍 or 👎 to teach Ellipsis.
1. CLAUDE.md:37
  • Draft comment:
    Removed extraneous trailing whitespace after the '@overload' decorator to improve formatting consistency.
  • Reason this comment was not posted:
    Comment did not seem useful. Confidence is useful = 0% <= threshold 50% This comment is purely informative, as it only describes a formatting change without suggesting any action or improvement. It doesn't provide any actionable feedback or raise any concerns about the code.
2. stubs/pythonista_stubs/appex.pyi:50
  • Draft comment:
    Removed the unused '_ImageType' alias and updated the return type in get_image_data to use 'bytes | None', which improves clarity.
  • Reason this comment was not posted:
    Comment did not seem useful. Confidence is useful = 0% <= threshold 50% This comment is purely informative, explaining what changes were made without providing any actionable feedback or suggestions. It doesn't align with the rules for useful comments.
3. stubs/pythonista_stubs/cb.pyi:7
  • Draft comment:
    Refined the type ignore directive by specifying '[import-not-found]', which is more explicit.
  • Reason this comment was not posted:
    Comment did not seem useful. Confidence is useful = 0% <= threshold 50% This comment is purely informative, explaining a change that was made without suggesting any action or asking for clarification. It doesn't provide a suggestion or raise a concern about the code.
4. stubs/pythonista_stubs/clipboard.pyi:2
  • Draft comment:
    Removed the try/except fallback for the PIL.Image import, assuming the Pillow library is available in the target environment.
  • Reason this comment was not posted:
    Comment did not seem useful. Confidence is useful = 0% <= threshold 50% This comment is purely informative, stating what was done without suggesting any changes or asking for confirmation. It doesn't provide any actionable feedback or raise any specific concerns.
5. stubs/pythonista_stubs/console.pyi:232
  • Draft comment:
    Updated union return types (e.g. 'Optional[str]' replaced with 'str | None') in functions like 'open_in' for consistent type hinting.
  • Reason this comment was not posted:
    Comment did not seem useful. Confidence is useful = 0% <= threshold 50% This comment is purely informative, as it only describes a change that was made without providing any actionable feedback or suggestions. It does not ask for confirmation or suggest improvements.
6. stubs/pythonista_stubs/contacts.pyi:117
  • Draft comment:
    Enhanced docstrings in 'get_group' and 'get_person' to explicitly state that 'None' is returned when no matching record is found.
  • Reason this comment was not posted:
    Comment did not seem useful. Confidence is useful = 0% <= threshold 50% This comment is purely informative, as it only describes a change made to docstrings without suggesting any action or improvement. It doesn't ask for confirmation or suggest any code changes.
7. stubs/pythonista_stubs/dialogs.pyi:125
  • Draft comment:
    Refactored dialog field dictionaries by introducing TypedDicts (e.g., TextFieldDict, SwitchFieldDict) to improve type precision and clarity.
  • Reason this comment was not posted:
    Comment did not seem useful. Confidence is useful = 0% <= threshold 50% This comment is purely informative, describing what was done in the code without providing any actionable feedback or suggestions. It does not align with the rules for useful comments.
8. stubs/pythonista_stubs/editor.pyi:11
  • Draft comment:
    Applied consistent union type annotations (e.g., 'str | None', 'tuple[int, int] | None') for function return types to enhance readability.
  • Reason this comment was not posted:
    Comment did not seem useful. Confidence is useful = 0% <= threshold 50% This comment is purely informative, explaining what was done in the code without providing any actionable feedback or suggestions. It doesn't ask for confirmation or suggest improvements.
9. stubs/pythonista_stubs/keyboard.pyi:40
  • Draft comment:
    Replaced the older 'Optional' syntax with the union '|' syntax in return types and parameters, making the annotations clearer.
  • Reason this comment was not posted:
    Comment was not on a location in the diff, so it can't be submitted as a review comment.
10. stubs/pythonista_stubs/linguistictagger.pyi:40
  • Draft comment:
    Updated type hints to use built-in generics (e.g., 'list[tuple[str, str, tuple[int, int]]]' instead of List[...] and Tuple[...]), aligning with PEP 585.
  • Reason this comment was not posted:
    Comment did not seem useful. Confidence is useful = 0% <= threshold 50% This comment is purely informative, explaining a change that aligns with PEP 585. It does not provide a suggestion, ask for confirmation, or highlight a potential issue.
11. stubs/pythonista_stubs/location.pyi:16
  • Draft comment:
    Updated union annotations to use 'dict[str, float] | None' instead of Optional[Dict[str, float]], improving clarity.
  • Reason this comment was not posted:
    Comment did not seem useful. Confidence is useful = 0% <= threshold 50% This comment is purely informative, explaining a change that was made without suggesting any action or asking for clarification. It doesn't provide any actionable feedback or raise any concerns about the code.
12. stubs/pythonista_stubs/notification.pyi:32
  • Draft comment:
    Standardized list and dict type annotations in the 'schedule' function using modern union syntax for better consistency.
  • Reason this comment was not posted:
    Comment did not seem useful. Confidence is useful = 0% <= threshold 50% This comment is purely informative, describing a change made in the code without providing any actionable feedback or suggestions. It doesn't ask for confirmation or suggest improvements.
13. stubs/pythonista_stubs/objc_util.pyi:19
  • Draft comment:
    Updated the 'on_main_thread' decorator to use the _F type variable and removed redundant fields definitions from struct classes, which simplifies the stubs.
  • Reason this comment was not posted:
    Comment did not seem useful. Confidence is useful = 0% <= threshold 50% This comment is purely informative, describing what changes were made without providing any actionable feedback or suggestions. It doesn't ask for confirmation or suggest improvements.
14. stubs/pythonista_stubs/photos.pyi:130
  • Draft comment:
    Removed the unused _MapType alias to clean up the stub file.
  • Reason this comment was not posted:
    Comment did not seem useful. Confidence is useful = 0% <= threshold 50% This comment is purely informative, stating that an unused alias was removed to clean up the file. It doesn't provide any actionable feedback or suggestions for improvement.
15. stubs/pythonista_stubs/reminders.pyi:118
  • Draft comment:
    Clarified the return type in 'get_calendar' to 'Calendar | None' within the docstring, making the behavior explicit.
  • Reason this comment was not posted:
    Comment was not on a location in the diff, so it can't be submitted as a review comment.
16. stubs/pythonista_stubs/sound.pyi:27
  • Draft comment:
    Updated the return type in 'play_effect' from Optional[Effect] to 'Effect | None' to adhere to the new union syntax.
  • Reason this comment was not posted:
    Comment did not seem useful. Confidence is useful = 0% <= threshold 50% This comment is purely informative, explaining a change in syntax from Optional[Effect] to 'Effect | None'. It does not provide a suggestion, ask for confirmation, or highlight a potential issue. It simply states what was done, which violates the rule against purely informative comments.
17. stubs/pythonista_stubs/ui.pyi:11
  • Draft comment:
    Changed class method return types in Image (from string forward references to direct type references) to improve static analysis accuracy.
  • Reason this comment was not posted:
    Comment did not seem useful. Confidence is useful = 0% <= threshold 50% This comment is purely informative, explaining a change made in the code. It does not provide a suggestion, ask for confirmation, or point out a potential issue. According to the rules, purely informative comments should be removed.

Workflow ID: wflow_h0yE7V1LFjyL410E

You can customize Ellipsis by changing your verbosity settings, reacting with 👍 or 👎, replying to comments, or adding code review rules.

@hbmartin hbmartin merged commit e50bf9a into master Aug 12, 2025
1 of 2 checks passed
@hbmartin hbmartin deleted the hm/ruff-formatting-other-fixes branch August 12, 2025 15:02
@o-murphy
Copy link
Copy Markdown
Collaborator

Shared Central madager not documented but exist! There are a lot of undocumented built-ins or modules. We even have not a half of it annotated

Just try:

import cb
help(cb)

image

@o-murphy
Copy link
Copy Markdown
Collaborator

>>> import cb
>>> help(cb)
Help on module cb:

NAME
    cb - #import 'pythonista'

CLASSES
    _cb._CentralManager(builtins.object)
        SharedCentralManager

    class SharedCentralManager(_cb._CentralManager)
     |  Method resolution order:
     |      SharedCentralManager
     |      _cb._CentralManager
     |      builtins.object
     |
     |  Methods defined here:
     |
     |  __init__(self)
     |      Initialize self.  See help(type(self)) for accurate signature.
     |
     |  did_connect_peripheral(self, p)
     |
     |  did_disconnect_peripheral(self, p, error)
     |
     |  did_discover_characteristics(self, s, error)
     |
     |  did_discover_peripheral(self, p)
     |
     |  did_discover_services(self, p, error)
     |
     |  did_fail_to_connect_peripheral(self, p, error)
     |
     |  did_update_state(self)
     |
     |  did_update_value(self, c, error)
     |
     |  did_write_value(self, c, error)
     |
     |  verbose_log(self, msg)
     |
     |  ----------------------------------------------------------------------
     |  Data descriptors defined here:
     |
     |  __dict__
     |      dictionary for instance variables
     |
     |  __weakref__
     |      list of weak references to the object
     |
     |  ----------------------------------------------------------------------
     |  Methods inherited from _cb._CentralManager:
     |
     |  __eq__(self, value, /)
     |      Return self==value.
     |
     |  __ge__(self, value, /)
     |      Return self>=value.
     |
     |  __gt__(self, value, /)
     |      Return self>value.
     |
     |  __le__(self, value, /)
     |      Return self<=value.
     |
     |  __lt__(self, value, /)
     |      Return self<value.
     |
     |  __ne__(self, value, /)
     |      Return self!=value.
     |
     |  cancel_peripheral_connection(...)
     |
     |  connect_peripheral(...)
     |
     |  scan_for_peripherals(...)
     |
     |  stop_scan(self, /)
     |
     |  ----------------------------------------------------------------------
     |  Static methods inherited from _cb._CentralManager:
     |
     |  __new__(*args, **kwargs) class method of _cb._CentralManager
     |      Create and return a new object.  See help(type) for accurate signature.
     |
     |  ----------------------------------------------------------------------
     |  Data descriptors inherited from _cb._CentralManager:
     |
     |  state
     |
     |  ----------------------------------------------------------------------
     |  Data and other attributes inherited from _cb._CentralManager:
     |
     |  __hash__ = None

FUNCTIONS
    cancel_peripheral_connection(peripheral)

    connect_peripheral(peripheral)

    get_state()

    reset()

    scan_for_peripherals()

    set_central_delegate(delegate)

    set_verbose(verbose)

    stop_scan()

DATA
    CH_PROP_AUTHENTICATED_SIGNED_WRITES = 64
    CH_PROP_BROADCAST = 1
    CH_PROP_EXTENDED_PROPERTIES = 128
    CH_PROP_INDICATE = 32
    CH_PROP_INDICATE_ENCRYPTION_REQUIRED = 512
    CH_PROP_NOTIFY = 16
    CH_PROP_NOTIFY_ENCRYPTION_REQUIRED = 256
    CH_PROP_READ = 2
    CH_PROP_WRITE = 8
    CH_PROP_WRITE_WITHOUT_RESPONSE = 4
    CM_STATE_POWERED_OFF = 4
    CM_STATE_POWERED_ON = 5
    CM_STATE_RESETTING = 1
    CM_STATE_UNAUTHORIZED = 3
    CM_STATE_UNKNOWN = 0
    CM_STATE_UNSUPPORTED = 2
    shared_manager = <cb.SharedCentralManager object>

FILE
    /var/containers/Bundle/Application/35A78A58-4849-4F8D-961F-DD2A1CB0655F/Pythonista3.app/Frameworks/Py3Kit.framework/pylib/site-packages/pythonista/cb.py

@o-murphy
Copy link
Copy Markdown
Collaborator

o-murphy commented Aug 12, 2025

Same for_cb.py

I know it cause I developed bleak-compatible backend based on pythonista cb

#import 'pythonista'
from _cb import *

shared_manager = None
reset = None

class SharedCentralManager (CentralManager):
	def __init__(self):
		self.delegate = None
		self.verbose = False
	
	def verbose_log(self, msg):
		if self.verbose:
			print(msg)
	
	def did_update_state(self):
		self.verbose_log('CB: Did update state: %i' % self.state)
		try:
			self.delegate.did_update_state()
		except AttributeError:
			pass
		except:
			reset()
			raise
	
	def did_discover_peripheral(self, p):
		self.verbose_log('CB: Did discover peripheral: %s (%s)' % (p.name, p.uuid))
		try:
			self.delegate.did_discover_peripheral(p)
		except AttributeError:
			pass
		except:
			reset()
			raise
	
	def did_connect_peripheral(self, p):
		self.verbose_log('CB: Did connect peripheral: %s (%s)' % (p.name, p.uuid))
		try:
			self.delegate.did_connect_peripheral(p)
		except AttributeError:
			pass
		except:
			reset()
			raise
		
	
	def did_fail_to_connect_peripheral(self, p, error):
		self.verbose_log('CB: Did fail to connect peripheral: %s (%s) -- Error: %s' % (p.name, p.uuid, error))
		try:
			self.delegate.did_fail_to_connect_peripheral(p, error)
		except AttributeError:
			pass
		except:
			reset()
			raise
	
	def did_disconnect_peripheral(self, p, error):
		self.verbose_log('CB: Did disconnect peripheral: %s (%s) -- Error: %s' % (p.name, p.uuid, error))
		try:
			self.delegate.did_disconnect_peripheral(p, error)
		except AttributeError:
			pass
		except:
			reset()
			raise
	
	def did_discover_services(self, p, error):
		self.verbose_log('CB: Did discover services for peripheral: %s (%s)' % (p.name, p.uuid))
		try:
			self.delegate.did_discover_services(p, error)
		except AttributeError:
			pass
		except:
			reset()
			raise
	
	def did_discover_characteristics(self, s, error):
		self.verbose_log('CB: Did discover characteristics for service: %s' % (s.uuid,))
		try:
			self.delegate.did_discover_characteristics(s, error)
		except AttributeError:
			pass
		except:
			reset()
			raise
	
	def did_write_value(self, c, error):
		self.verbose_log('CB: Did write value for characteristic: %s' % c.uuid)
		try:
			self.delegate.did_write_value(c, error)
		except AttributeError:
			pass
		except:
			reset()
			raise
	
	def did_update_value(self, c, error):
		self.verbose_log('CB: Did update value for characteristic: %s' % c.uuid)
		try:
			self.delegate.did_update_value(c, error)
		except AttributeError:
			pass
		except:
			reset()
			raise

shared_manager = SharedCentralManager()

def set_central_delegate(delegate):
	shared_manager.delegate = delegate

def set_verbose(verbose):
	shared_manager.verbose = verbose

def scan_for_peripherals():
	shared_manager.scan_for_peripherals()

def stop_scan():
	shared_manager.stop_scan()

def connect_peripheral(peripheral):
	shared_manager.connect_peripheral(peripheral)

def cancel_peripheral_connection(peripheral):
	shared_manager.cancel_peripheral_connection(peripheral)

def get_state():
	return shared_manager.state

def reset():
	global shared_manager
	shared_manager.delegate = None
	del shared_manager
	shared_manager = SharedCentralManager()

@o-murphy
Copy link
Copy Markdown
Collaborator

The documented methons like set_central_delagate are operate over undocumented SharedCentralManager singleton

@o-murphy
Copy link
Copy Markdown
Collaborator

For example the documented keyboard module works as wrapper to built-in _keyboard module

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants