Skip to content

[refactor] util: split __init__.py into geometry and concurrent modules#3474

Open
tepals wants to merge 1 commit into
delmic:masterfrom
tepals:SWM-221-split-util-init
Open

[refactor] util: split __init__.py into geometry and concurrent modules#3474
tepals wants to merge 1 commit into
delmic:masterfrom
tepals:SWM-221-split-util-init

Conversation

@tepals
Copy link
Copy Markdown
Contributor

@tepals tepals commented May 28, 2026

Extract 2D geometry functions into a new geometry.py module:

  • Rectangle operations: rect_intersect, intersect, normalize_rect, expand_rect, is_point_in_rect, rotate_rect, separate_rect_rotation
  • Line/polygon operations: perpendicular_distance, clip_line, get_polygon_bbox, slope_of_line, intercept_of_line, project_point_on_line
  • Constants: INSIDE, LEFT, RIGHT, LOWER, UPPER

Extract concurrency utilities into a new concurrent.py module:

  • Rate limiting: limit_invocation
  • Threading: RepeatingTimer, BackgroundWorker
  • Future helpers: executeAsyncTask, bindFuture
  • Decorators: timeout
  • Introspection: inspect_getmembers

General-purpose helpers remain in init.py

All moved symbols are re-exported from odemis.util for backward compatibility.

Co-authored-by: Copilot

@tepals tepals added the llm-delegated When the proposed changes are almost exclusively LLM generated. label May 28, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 28, 2026

Review Change Stack

📝 Walkthrough

Walkthrough

This PR extracts concurrency utilities into src/odemis/util/concurrent.py and 2D geometry utilities into src/odemis/util/geometry.py, and refactors src/odemis/util/init.py to re-export those helpers for backward compatibility. concurrent.py adds rate-limiting and timeout decorators, RepeatingTimer, BackgroundWorker, executeAsyncTask/bindFuture, and inspect_getmembers. geometry.py adds rectangle/line/polygon helpers (intersection, clipping, rotation, decomposing rotated rectangles, bbox). init.py retains a small set of helpers and now re-exports the new modules.

sequenceDiagram
  participant Caller
  participant limit_invocation_wrapper
  participant instance_queue
  participant worker_thread
  participant target_function
  Caller->>limit_invocation_wrapper: call(...)
  limit_invocation_wrapper->>instance_queue: enqueue(call,args)
  instance_queue->>worker_thread: dequeue(next_call)
  worker_thread->>target_function: invoke(args)
  worker_thread->>Caller: set_result / propagate exception
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 87.27% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed The pull request description clearly outlines the refactoring effort: extracting 2D geometry functions into geometry.py, concurrency utilities into concurrent.py, re-exporting from init.py for backward compatibility, and keeping general-purpose helpers in init.py. This aligns directly with the changeset.
Title check ✅ Passed The title accurately describes the main refactoring work: extracting geometry and concurrent modules from init.py, matching the primary objective of the PR.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

❤️ Share

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

Copy link
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: 6

🧹 Nitpick comments (1)
src/odemis/util/concurrent.py (1)

149-149: 💤 Low value

Consider using spread syntax for tuple construction.

The spread syntax is clearer and avoids explicit tuple concatenation.

✨ Suggested change
-                    q.put((now, f, (self,) + args, kwargs))
+                    q.put((now, f, (self, *args), kwargs))
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/odemis/util/concurrent.py` at line 149, Replace the explicit tuple
concatenation (self,) + args with tuple unpacking to construct the arguments
tuple more readably; in the q.put call in concurrent.py (the expression
currently building (now, f, (self,) + args, kwargs)), change the third element
to use spread/unpacking like (self, *args) so the queue entry becomes (now, f,
(self, *args), kwargs).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/odemis/util/concurrent.py`:
- Line 313: The function signature for executeAsyncTask uses an implicit
Optional for kwargs; update the type annotation to explicitly allow None (e.g.,
change kwargs: dict to kwargs: dict | None = None) so the default None is
represented with a union type; keep the function name executeAsyncTask and
existing defaults intact when making this change.
- Line 334: The bindFuture signature uses a bare "dict = None" for kwargs;
update the type annotation to explicitly allow None (match executeAsyncTask) by
changing the parameter to kwargs: dict | None = None so the type is an explicit
union with None, and ensure any references in the function body handle kwargs
possibly being None.
- Around line 368-430: Add static typing to both inspect_getmembers
implementations: annotate the first parameter (rename from object to obj to
avoid shadowing) as Any or types.ModuleType/Type[Any] as appropriate, annotate
predicate as Optional[Callable[[Any], bool]], and set the return type to
List[Tuple[str, Any]] (import typing names). Update both function signatures for
the Python >=3.11 and <=3.10 branches and add necessary imports (from typing
import Any, Callable, List, Optional, Tuple) at the top of the module.

In `@src/odemis/util/geom.py`:
- Around line 110-124: The nested helper _get_pos(xa, ya) lacks type hints and a
docstring; add parameter and return type annotations (e.g., xa: float, ya: float
-> int or appropriate enum/bitmask type) and insert a concise reStructuredText
docstring above the body describing parameters, return value, and the bitflags
(INSIDE, LEFT, RIGHT, LOWER, UPPER) behavior; keep the function name _get_pos
and references to xmin, xmax, ymin, ymax unchanged and ensure the docstring
explains these globals are used to determine the bitmask returned.
- Around line 274-307: The function separate_rect_rotation assumes exactly 4
corner points but indexes corners[3] without validation; add an explicit check
at the start of separate_rect_rotation to validate that len(corners) == 4 (or at
least >=4 if you prefer) and raise a clear ValueError (e.g.,
"separate_rect_rotation requires 4 corner points, got N") before any indexing,
so callers get an actionable error instead of an IndexError when corners is
malformed.
- Around line 189-205: Add explicit type hints to normalize_rect: import TypeVar
and Tuple from typing, declare a type variable like R = TypeVar("R",
bound=Tuple[float, float, float, float]) and annotate the signature as def
normalize_rect(rect: R) -> R:, keeping the existing implementation that returns
type(rect)((x1, y1, x2, y2)). This ensures the parameter is a 4-float tuple type
and the return type matches the input while satisfying the repo's typing
guideline; update imports accordingly.

---

Nitpick comments:
In `@src/odemis/util/concurrent.py`:
- Line 149: Replace the explicit tuple concatenation (self,) + args with tuple
unpacking to construct the arguments tuple more readably; in the q.put call in
concurrent.py (the expression currently building (now, f, (self,) + args,
kwargs)), change the third element to use spread/unpacking like (self, *args) so
the queue entry becomes (now, f, (self, *args), kwargs).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 08edf0a9-df5b-4d61-98ee-dc1cdb07b12d

📥 Commits

Reviewing files that changed from the base of the PR and between 3e12d1e and b49a5f9.

📒 Files selected for processing (3)
  • src/odemis/util/__init__.py
  • src/odemis/util/concurrent.py
  • src/odemis/util/geom.py

Comment thread src/odemis/util/concurrent.py
Comment thread src/odemis/util/concurrent.py
Comment thread src/odemis/util/concurrent.py
Comment thread src/odemis/util/geometry.py
Comment thread src/odemis/util/geometry.py
Comment thread src/odemis/util/geometry.py
Copy link
Copy Markdown
Member

@pieleric pieleric left a comment

Choose a reason for hiding this comment

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

Looks to me like a good separation. I only have one small suggestion.

Comment thread src/odemis/util/geometry.py
@tepals tepals force-pushed the SWM-221-split-util-init branch from b49a5f9 to 6f24fbc Compare May 29, 2026 07:09
@tepals tepals marked this pull request as ready for review May 29, 2026 07:10
Copilot AI review requested due to automatic review settings May 29, 2026 07:10
@tepals tepals requested a review from tmoerkerken May 29, 2026 07:12
Copy link
Copy Markdown

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

Refactors odemis.util by moving geometry- and concurrency-related helpers out of the package __init__.py into dedicated modules, while keeping backwards compatibility via re-exports from odemis.util.

Changes:

  • Added src/odemis/util/geometry.py containing 2D rectangle/line/polygon helpers and related constants.
  • Added src/odemis/util/concurrent.py containing rate-limiting, timeout, timer/worker utilities, Future helpers, and an inspect.getmembers compatibility wrapper.
  • Updated src/odemis/util/__init__.py to re-export moved symbols and removed the inlined implementations.

Reviewed changes

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

File Description
src/odemis/util/geometry.py New module for 2D geometry utilities previously in odemis.util.
src/odemis/util/concurrent.py New module for concurrency/timing/Future helpers previously in odemis.util.
src/odemis/util/init.py Re-exports moved symbols for backward compatibility and keeps general helpers local.

Comment thread src/odemis/util/concurrent.py
Comment thread src/odemis/util/concurrent.py
Comment thread src/odemis/util/geometry.py
Comment thread src/odemis/util/geometry.py
Comment thread src/odemis/util/__init__.py
 Extract 2D geometry functions into a new `geometry.py` module:
 - Rectangle operations: rect_intersect, intersect, normalize_rect, expand_rect,
   is_point_in_rect, rotate_rect, separate_rect_rotation
 - Line/polygon operations: perpendicular_distance, clip_line, get_polygon_bbox,
   slope_of_line, intercept_of_line, project_point_on_line
 - Constants: INSIDE, LEFT, RIGHT, LOWER, UPPER

 Extract concurrency utilities into a new `concurrent.py` module:
 - Rate limiting: limit_invocation
 - Threading: RepeatingTimer, BackgroundWorker
 - Future helpers: executeAsyncTask, bindFuture
 - Decorators: timeout
 - Introspection: inspect_getmembers

General-purpose helpers remain in __init__.py

 All moved symbols are re-exported from `odemis.util` for backward compatibility.

 Co-authored-by: Copilot
@tepals tepals force-pushed the SWM-221-split-util-init branch from 6f24fbc to 58ede4c Compare May 29, 2026 07:17
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: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/odemis/util/concurrent.py`:
- Line 87: Add explicit return type hints to the decorator factories
`limit_invocation` and `timeout`: declare a TypeVar (e.g., F bound to
Callable[..., Any]) and annotate each function to return a decorator of type
Callable[[F], F] (or the equivalent Callable[[Callable[..., Any]], Callable[...,
Any]]), and add any needed typing imports (TypeVar, Callable, Any) at the top;
update the signatures for `limit_invocation` and `timeout` accordingly so static
checkers know they return a decorator that accepts and returns the original
callable type.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f9a77d7f-6d3b-4553-9b3b-5c772f3bf35a

📥 Commits

Reviewing files that changed from the base of the PR and between b49a5f9 and 6f24fbc.

📒 Files selected for processing (3)
  • src/odemis/util/__init__.py
  • src/odemis/util/concurrent.py
  • src/odemis/util/geometry.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/odemis/util/init.py

logging.debug("Ending li thread")


def limit_invocation(delay_s: float):
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 | 🟠 Major | ⚡ Quick win

Add return type hints to the decorator factories.

limit_invocation (Line 87) and timeout (Line 167) both return a decorator but omit the return annotation. As per coding guidelines: "Always use type hints for function parameters and return types in Python code".

♻️ Proposed annotations
-def limit_invocation(delay_s: float):
+def limit_invocation(delay_s: float) -> Callable:
-def timeout(seconds: float):
+def timeout(seconds: float) -> Callable:
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/odemis/util/concurrent.py` at line 87, Add explicit return type hints to
the decorator factories `limit_invocation` and `timeout`: declare a TypeVar
(e.g., F bound to Callable[..., Any]) and annotate each function to return a
decorator of type Callable[[F], F] (or the equivalent Callable[[Callable[...,
Any]], Callable[..., Any]]), and add any needed typing imports (TypeVar,
Callable, Any) at the top; update the signatures for `limit_invocation` and
`timeout` accordingly so static checkers know they return a decorator that
accepts and returns the original callable type.

@tepals tepals changed the title [refactor] util: split __init__.py into geom and concurrent modules [refactor] util: split __init__.py into geometry and concurrent modules May 29, 2026
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.

🧹 Nitpick comments (1)
src/odemis/util/geometry.py (1)

96-108: ⚡ Quick win

Document clip_line's parameters, especially the unusual argument order.

The signature takes xmin, ymax, xmax, ymin (not the conventional xmin, ymin, xmax, ymax), and the docstring documents neither the parameters nor the return value. This is an easy footgun for callers.

Proposed fix
     This implements the Cohen-Sutherland line clipping algorithm. Although it's not the most
     efficient clipping algorithm, it was chosen because it's best at cheaply determining the trivial
     cases (line being completely inside or outside the bounding box).
 
+    Note the unusual parameter order: xmin, ymax, xmax, ymin.
+
+    :param xmin: minimum x of the clipping rectangle
+    :param ymax: maximum y of the clipping rectangle
+    :param xmax: maximum x of the clipping rectangle
+    :param ymin: minimum y of the clipping rectangle
+    :param x1: x of the first endpoint of the line
+    :param y1: y of the first endpoint of the line
+    :param x2: x of the second endpoint of the line
+    :param y2: y of the second endpoint of the line
+    :returns: clipped line as (x1, y1, x2, y2) rounded to integers, or
+        (None, None, None, None) if the line lies entirely outside the rectangle
+
     Code based on https://github.com/scienceopen/cv-utils/blob/master/lineClipping.py
     Copyright (c) 2014 Michael Hirsch
     """

As per coding guidelines: "Include docstrings for all functions and classes, following the reStructuredText style guide...".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/odemis/util/geometry.py` around lines 96 - 108, Add a full
reStructuredText docstring to clip_line describing each parameter (explicitly
call out the unusual order xmin, ymax, xmax, ymin), the point coordinates (x1,
y1, x2, y2), expected types, and the return tuple meaning (clipped x1, y1, x2,
y2 with None when fully outside) plus any exceptions/edge cases; update the top
of the clip_line docstring to include a short summary, param: entries for xmin,
ymax, xmax, ymin, x1, y1, x2, y2, and a returns: section describing the
Tuple[Optional[int], Optional[int], Optional[int], Optional[int]] semantics,
ensuring wording follows the reStructuredText style guide.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/odemis/util/geometry.py`:
- Around line 96-108: Add a full reStructuredText docstring to clip_line
describing each parameter (explicitly call out the unusual order xmin, ymax,
xmax, ymin), the point coordinates (x1, y1, x2, y2), expected types, and the
return tuple meaning (clipped x1, y1, x2, y2 with None when fully outside) plus
any exceptions/edge cases; update the top of the clip_line docstring to include
a short summary, param: entries for xmin, ymax, xmax, ymin, x1, y1, x2, y2, and
a returns: section describing the Tuple[Optional[int], Optional[int],
Optional[int], Optional[int]] semantics, ensuring wording follows the
reStructuredText style guide.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f85445ed-3bfe-4fab-8454-ebb316796b6f

📥 Commits

Reviewing files that changed from the base of the PR and between 6f24fbc and 58ede4c.

📒 Files selected for processing (3)
  • src/odemis/util/__init__.py
  • src/odemis/util/concurrent.py
  • src/odemis/util/geometry.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/odemis/util/init.py

@tepals tepals requested a review from pieleric May 29, 2026 07:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

llm-delegated When the proposed changes are almost exclusively LLM generated. size/L

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants