This is an automated email from the ASF dual-hosted git repository.

linxinyuan pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/texera.git


The following commit(s) were added to refs/heads/main by this push:
     new 8530645dbf feat: Lint Python with Ruff (#4105)
8530645dbf is described below

commit 8530645dbf15e258bd90e441058168a7202de512
Author: LJX2017 <[email protected]>
AuthorDate: Fri Dec 5 21:26:24 2025 -0800

    feat: Lint Python with Ruff (#4105)
    
    <!--
    Thanks for sending a pull request (PR)! Here are some tips for you:
    1. If this is your first time, please read our contributor guidelines:
    [Contributing to
    Texera](https://github.com/apache/texera/blob/main/CONTRIBUTING.md)
      2. Ensure you have added or run the appropriate tests for your PR
      3. If the PR is work in progress, mark it a draft on GitHub.
      4. Please write your PR title to summarize what this PR proposes, we
        are following Conventional Commits style for PR titles as well.
      5. Be sure to keep the PR description updated to reflect all changes.
    -->
    
    ### What changes were proposed in this PR?
    <!--
    Please clarify what changes you are proposing. The purpose of this
    section
    is to outline the changes. Here are some tips for you:
      1. If you propose a new API, clarify the use case for a new API.
      2. If you fix a bug, you can clarify why it is a bug.
      3. If it is a refactoring, clarify what has been changed.
      3. It would be helpful to include a before-and-after comparison using
         screenshots or GIFs.
      4. Please consider writing useful notes for better and faster reviews.
    -->
    1. Replace flake8 and black with Ruff in CI.
    2. Format existing code using Ruff
    
    Basic Ruff commands:
    Under amber/src/main/python
    ```cd amber/src/main/python```
    Run Ruff’s formatter in dry mode
    ```ruff format --check .```
    Run Ruff’s formatter
    ```ruff format .```
    Run Ruff’s linter
    ```ruff check .```
    
    ### Any related issues, documentation, discussions?
    <!--
    Please use this section to link other resources if not mentioned
    already.
    1. If this PR fixes an issue, please include `Fixes #1234`, `Resolves
    #1234`
    or `Closes #1234`. If it is only related, simply mention the issue
    number.
      4. If there is design documentation, please add the link.
      5. If there is a discussion in the mailing list, please add the link.
    -->
    Closes #4078
    
    ### How was this PR tested?
    <!--
    If tests were added, say they were added here. Or simply mention that if
    the PR
    is tested with existing test cases. Make sure to include/update test
    cases that
    check the changes thoroughly including negative and positive cases if
    possible.
    If it was tested in a way different from regular unit tests, please
    clarify how
    you tested step by step, ideally copy and paste-able, so that other
    reviewers can
    test and check, and descendants can verify in the future. If tests were
    not added,
    please describe why they were not added and/or why it was difficult to
    add.
    -->
    I created a PR on my own fork to ensure CI is working.
    
    ### Was this PR authored or co-authored using generative AI tooling?
    <!--
    If generative AI tooling has been used in the process of authoring this
    PR,
    please include the phrase: 'Generated-by: ' followed by the name of the
    tool
    and its version. If no, write 'No'.
    Please refer to the [ASF Generative Tooling
    Guidance](https://www.apache.org/legal/generative-tooling.html) for
    details.
    -->
    No
    
    ---------
    
    Co-authored-by: Xinyuan Lin <[email protected]>
---
 .github/workflows/github-action-build.yml          |  4 +--
 amber/requirements.txt                             |  4 +--
 .../handlers/control/add_partitioning_handler.py   |  1 -
 .../handlers/control/assign_port_handler.py        |  1 -
 .../control/evaluate_expression_handler.py         |  1 -
 .../control/initialize_executor_handler.py         |  1 -
 .../handlers/control/no_operation_handler.py       |  1 -
 .../handlers/control/open_executor_handler.py      |  1 -
 .../handlers/control/pause_worker_handler.py       |  1 -
 .../handlers/control/query_statistics_handler.py   |  1 -
 .../control/replay_current_tuple_handler.py        |  1 -
 .../handlers/control/resume_worker_handler.py      |  1 -
 .../managers/embedded_control_message_manager.py   |  6 ++--
 .../core/architecture/managers/executor_manager.py | 12 ++++----
 .../core/architecture/packaging/input_manager.py   |  1 -
 .../core/architecture/rpc/async_rpc_client.py      |  1 -
 .../src/main/python/core/models/internal_queue.py  |  1 -
 amber/src/main/python/core/models/tuple.py         |  6 ++--
 amber/src/main/python/core/runnables/main_loop.py  |  6 ++--
 .../main/python/core/runnables/test_main_loop.py   |  2 --
 .../core/storage/iceberg/test_iceberg_document.py  | 34 ++++++++++------------
 .../src/main/python/core/storage/storage_config.py |  2 +-
 amber/src/main/python/pyproject.toml               | 20 +++++--------
 .../pytexera/storage/dataset_file_document.py      |  3 +-
 bin/fix-format.sh                                  |  8 ++---
 25 files changed, 47 insertions(+), 73 deletions(-)

diff --git a/.github/workflows/github-action-build.yml 
b/.github/workflows/github-action-build.yml
index ff533a109f..4c79fdce80 100644
--- a/.github/workflows/github-action-build.yml
+++ b/.github/workflows/github-action-build.yml
@@ -152,9 +152,9 @@ jobs:
       - name: Create Database and User
         run: |
           cd sql && sudo -u postgres psql -f iceberg_postgres_catalog.sql
-      - name: Lint with flake8 and black
+      - name: Lint with Ruff
         run: |
-          cd amber/src/main/python && flake8 && black . --check
+          cd amber/src/main/python && ruff check . && ruff format --check .
       - name: Test with pytest
         run: |
           cd amber/src/main/python && pytest -sv
diff --git a/amber/requirements.txt b/amber/requirements.txt
index 2b85f84606..02aacdc7aa 100644
--- a/amber/requirements.txt
+++ b/amber/requirements.txt
@@ -18,9 +18,7 @@
 wheel==0.41.2
 numpy==2.1.0
 pandas==2.2.3
-flake8==7.1.1
-Flake8-pyproject==1.2.3
-black==24.3.0
+ruff==0.14.7
 iniconfig==1.1.1
 loguru==0.7.0
 pyarrow==21.0.0
diff --git 
a/amber/src/main/python/core/architecture/handlers/control/add_partitioning_handler.py
 
b/amber/src/main/python/core/architecture/handlers/control/add_partitioning_handler.py
index 10b0d3fb0b..b4a88d0fe1 100644
--- 
a/amber/src/main/python/core/architecture/handlers/control/add_partitioning_handler.py
+++ 
b/amber/src/main/python/core/architecture/handlers/control/add_partitioning_handler.py
@@ -23,7 +23,6 @@ from proto.org.apache.texera.amber.engine.architecture.rpc 
import (
 
 
 class AddPartitioningHandler(ControlHandler):
-
     async def add_partitioning(self, req: AddPartitioningRequest) -> 
EmptyReturn:
         self.context.output_manager.add_partitioning(req.tag, req.partitioning)
         return EmptyReturn()
diff --git 
a/amber/src/main/python/core/architecture/handlers/control/assign_port_handler.py
 
b/amber/src/main/python/core/architecture/handlers/control/assign_port_handler.py
index df669735ff..73ebad26b3 100644
--- 
a/amber/src/main/python/core/architecture/handlers/control/assign_port_handler.py
+++ 
b/amber/src/main/python/core/architecture/handlers/control/assign_port_handler.py
@@ -26,7 +26,6 @@ from proto.org.apache.texera.amber.engine.architecture.rpc 
import (
 
 
 class AssignPortHandler(ControlHandler):
-
     async def assign_port(self, req: AssignPortRequest) -> EmptyReturn:
         if req.input:
             self.context.input_manager.add_input_port(
diff --git 
a/amber/src/main/python/core/architecture/handlers/control/evaluate_expression_handler.py
 
b/amber/src/main/python/core/architecture/handlers/control/evaluate_expression_handler.py
index 7d35a6c1b8..b5c63acc55 100644
--- 
a/amber/src/main/python/core/architecture/handlers/control/evaluate_expression_handler.py
+++ 
b/amber/src/main/python/core/architecture/handlers/control/evaluate_expression_handler.py
@@ -24,7 +24,6 @@ from proto.org.apache.texera.amber.engine.architecture.rpc 
import (
 
 
 class EvaluateExpressionHandler(ControlHandler):
-
     async def evaluate_python_expression(
         self, req: EvaluatePythonExpressionRequest
     ) -> EvaluatedValue:
diff --git 
a/amber/src/main/python/core/architecture/handlers/control/initialize_executor_handler.py
 
b/amber/src/main/python/core/architecture/handlers/control/initialize_executor_handler.py
index 0dd3e45287..2c2dc1ad3c 100644
--- 
a/amber/src/main/python/core/architecture/handlers/control/initialize_executor_handler.py
+++ 
b/amber/src/main/python/core/architecture/handlers/control/initialize_executor_handler.py
@@ -25,7 +25,6 @@ from proto.org.apache.texera.amber.engine.architecture.rpc 
import (
 
 
 class InitializeExecutorHandler(ControlHandler):
-
     async def initialize_executor(self, req: InitializeExecutorRequest) -> 
EmptyReturn:
         op_exec_with_code: OpExecWithCode = get_one_of(req.op_exec_init_info)
         self.context.executor_manager.initialize_executor(
diff --git 
a/amber/src/main/python/core/architecture/handlers/control/no_operation_handler.py
 
b/amber/src/main/python/core/architecture/handlers/control/no_operation_handler.py
index 353e6eec9c..926ceb6153 100644
--- 
a/amber/src/main/python/core/architecture/handlers/control/no_operation_handler.py
+++ 
b/amber/src/main/python/core/architecture/handlers/control/no_operation_handler.py
@@ -23,6 +23,5 @@ from proto.org.apache.texera.amber.engine.architecture.rpc 
import (
 
 
 class NoOperationHandler(ControlHandler):
-
     async def no_operation(self, req: EmptyRequest) -> EmptyReturn:
         return EmptyReturn()
diff --git 
a/amber/src/main/python/core/architecture/handlers/control/open_executor_handler.py
 
b/amber/src/main/python/core/architecture/handlers/control/open_executor_handler.py
index 7c76fd58b9..2b178da556 100644
--- 
a/amber/src/main/python/core/architecture/handlers/control/open_executor_handler.py
+++ 
b/amber/src/main/python/core/architecture/handlers/control/open_executor_handler.py
@@ -23,7 +23,6 @@ from proto.org.apache.texera.amber.engine.architecture.rpc 
import (
 
 
 class OpenExecutorHandler(ControlHandler):
-
     async def open_executor(self, req: EmptyRequest) -> EmptyReturn:
         self.context.executor_manager.executor.open()
         return EmptyReturn()
diff --git 
a/amber/src/main/python/core/architecture/handlers/control/pause_worker_handler.py
 
b/amber/src/main/python/core/architecture/handlers/control/pause_worker_handler.py
index db5f6e7c41..ef9188914e 100644
--- 
a/amber/src/main/python/core/architecture/handlers/control/pause_worker_handler.py
+++ 
b/amber/src/main/python/core/architecture/handlers/control/pause_worker_handler.py
@@ -24,7 +24,6 @@ from proto.org.apache.texera.amber.engine.architecture.rpc 
import (
 
 
 class PauseWorkerHandler(ControlHandler):
-
     async def pause_worker(self, req: EmptyRequest) -> WorkerStateResponse:
         self.context.pause_manager.pause(PauseType.USER_PAUSE)
         state = self.context.state_manager.get_current_state()
diff --git 
a/amber/src/main/python/core/architecture/handlers/control/query_statistics_handler.py
 
b/amber/src/main/python/core/architecture/handlers/control/query_statistics_handler.py
index e6f384509c..b636249d71 100644
--- 
a/amber/src/main/python/core/architecture/handlers/control/query_statistics_handler.py
+++ 
b/amber/src/main/python/core/architecture/handlers/control/query_statistics_handler.py
@@ -26,7 +26,6 @@ from proto.org.apache.texera.amber.engine.architecture.worker 
import (
 
 
 class QueryStatisticsHandler(ControlHandler):
-
     async def query_statistics(self, req: EmptyRequest) -> 
WorkerMetricsResponse:
         metrics = WorkerMetrics(
             worker_state=self.context.state_manager.get_current_state(),
diff --git 
a/amber/src/main/python/core/architecture/handlers/control/replay_current_tuple_handler.py
 
b/amber/src/main/python/core/architecture/handlers/control/replay_current_tuple_handler.py
index b5070df652..5a916c4094 100644
--- 
a/amber/src/main/python/core/architecture/handlers/control/replay_current_tuple_handler.py
+++ 
b/amber/src/main/python/core/architecture/handlers/control/replay_current_tuple_handler.py
@@ -29,7 +29,6 @@ from proto.org.apache.texera.amber.engine.architecture.worker 
import (
 
 
 class RetryCurrentTupleHandler(ControlHandler):
-
     async def retry_current_tuple(self, req: EmptyRequest) -> EmptyReturn:
         if not self.context.state_manager.confirm_state(WorkerState.COMPLETED):
             # chain the current input tuple back on top of the current 
iterator to
diff --git 
a/amber/src/main/python/core/architecture/handlers/control/resume_worker_handler.py
 
b/amber/src/main/python/core/architecture/handlers/control/resume_worker_handler.py
index c49d90af58..3ebaadb661 100644
--- 
a/amber/src/main/python/core/architecture/handlers/control/resume_worker_handler.py
+++ 
b/amber/src/main/python/core/architecture/handlers/control/resume_worker_handler.py
@@ -24,7 +24,6 @@ from proto.org.apache.texera.amber.engine.architecture.rpc 
import (
 
 
 class ResumeWorkerHandler(ControlHandler):
-
     async def resume_worker(self, req: EmptyRequest) -> WorkerStateResponse:
         self.context.pause_manager.resume(PauseType.USER_PAUSE)
         state = self.context.state_manager.get_current_state()
diff --git 
a/amber/src/main/python/core/architecture/managers/embedded_control_message_manager.py
 
b/amber/src/main/python/core/architecture/managers/embedded_control_message_manager.py
index 43acb1c4e1..8ba80f28ce 100644
--- 
a/amber/src/main/python/core/architecture/managers/embedded_control_message_manager.py
+++ 
b/amber/src/main/python/core/architecture/managers/embedded_control_message_manager.py
@@ -74,9 +74,9 @@ class EmbeddedControlMessageManager:
 
         return ecm_completed
 
-    def get_channels_within_scope(
-        self, ecm: EmbeddedControlMessage
-    ) -> Dict["ChannelIdentity", "Channel"].keys:
+    def get_channels_within_scope(self, ecm: EmbeddedControlMessage) -> Dict[
+        "ChannelIdentity", "Channel"
+    ].keys:
         if ecm.scope:
             upstreams = {
                 channel_id
diff --git 
a/amber/src/main/python/core/architecture/managers/executor_manager.py 
b/amber/src/main/python/core/architecture/managers/executor_manager.py
index fc07ce74f5..53e5a8903d 100644
--- a/amber/src/main/python/core/architecture/managers/executor_manager.py
+++ b/amber/src/main/python/core/architecture/managers/executor_manager.py
@@ -139,9 +139,9 @@ class ExecutorManager:
         executor: type(Operator) = self.load_executor_definition(code)
         self.executor = executor()
         self.executor.is_source = is_source
-        assert (
-            isinstance(self.executor, SourceOperator) == 
self.executor.is_source
-        ), "Please use SourceOperator API for source operators."
+        assert isinstance(self.executor, SourceOperator) == 
self.executor.is_source, (
+            "Please use SourceOperator API for source operators."
+        )
 
     def update_executor(self, code: str, is_source: bool) -> None:
         """
@@ -157,9 +157,9 @@ class ExecutorManager:
         executor: type(Operator) = self.load_executor_definition(code)
         self.executor = executor()
         self.executor.is_source = is_source
-        assert (
-            isinstance(self.executor, SourceOperator) == 
self.executor.is_source
-        ), "Please use SourceOperator API for source operators."
+        assert isinstance(self.executor, SourceOperator) == 
self.executor.is_source, (
+            "Please use SourceOperator API for source operators."
+        )
         # overwrite the internal state
         self.executor.__dict__ = original_internal_state
         # TODO:
diff --git a/amber/src/main/python/core/architecture/packaging/input_manager.py 
b/amber/src/main/python/core/architecture/packaging/input_manager.py
index b5ef115155..6cb6bdc08c 100644
--- a/amber/src/main/python/core/architecture/packaging/input_manager.py
+++ b/amber/src/main/python/core/architecture/packaging/input_manager.py
@@ -156,7 +156,6 @@ class InputManager:
     def process_data_payload(
         self, from_: ChannelIdentity, payload: DataPayload
     ) -> Iterator[Union[Tuple, InternalMarker]]:
-
         self._current_channel_id = from_
 
         if isinstance(payload, DataFrame):
diff --git a/amber/src/main/python/core/architecture/rpc/async_rpc_client.py 
b/amber/src/main/python/core/architecture/rpc/async_rpc_client.py
index 0b97c4c62d..f11c5afdea 100644
--- a/amber/src/main/python/core/architecture/rpc/async_rpc_client.py
+++ b/amber/src/main/python/core/architecture/rpc/async_rpc_client.py
@@ -136,7 +136,6 @@ class AsyncRPCClient:
         rpc_client = self  # to distinguish outer and inner self
 
         class Proxy(service_class):
-
             def __init__(self, target_actor: ActorVirtualIdentity):
                 self.target_actor = target_actor
 
diff --git a/amber/src/main/python/core/models/internal_queue.py 
b/amber/src/main/python/core/models/internal_queue.py
index 645b426366..abc1793ff6 100644
--- a/amber/src/main/python/core/models/internal_queue.py
+++ b/amber/src/main/python/core/models/internal_queue.py
@@ -57,7 +57,6 @@ T = TypeVar("T", bound=InternalQueueElement)
 
 
 class InternalQueue(IQueue):
-
     class DisableType(Enum):
         DISABLE_BY_PAUSE = 1
         DISABLE_BY_BACKPRESSURE = 2
diff --git a/amber/src/main/python/core/models/tuple.py 
b/amber/src/main/python/core/models/tuple.py
index e88f08286d..d6ae12862b 100644
--- a/amber/src/main/python/core/models/tuple.py
+++ b/amber/src/main/python/core/models/tuple.py
@@ -189,9 +189,9 @@ class Tuple:
         :param item: field name or field index
         :return: field value
         """
-        assert isinstance(
-            item, (int, str)
-        ), "field can only be retrieved by index or name"
+        assert isinstance(item, (int, str)), (
+            "field can only be retrieved by index or name"
+        )
 
         if isinstance(item, int):
             item: str = self.get_field_names()[item]
diff --git a/amber/src/main/python/core/runnables/main_loop.py 
b/amber/src/main/python/core/runnables/main_loop.py
index 6b77d88cd5..d73c655734 100644
--- a/amber/src/main/python/core/runnables/main_loop.py
+++ b/amber/src/main/python/core/runnables/main_loop.py
@@ -296,7 +296,7 @@ class MainLoop(StoppableQueueBlockingRunnable):
         command = ecm.command_mapping.get(self.context.worker_id)
         channel_id = self.context.current_input_channel_id
         logger.info(
-            f"receive channel ECM from {channel_id}," f" id = {ecm.id}, cmd = 
{command}"
+            f"receive channel ECM from {channel_id}, id = {ecm.id}, cmd = 
{command}"
         )
         if ecm.ecm_type != EmbeddedControlMessageType.NO_ALIGNMENT:
             self.context.pause_manager.pause_input_channel(
@@ -305,8 +305,7 @@ class MainLoop(StoppableQueueBlockingRunnable):
 
         if self.context.ecm_manager.is_ecm_aligned(channel_id, ecm):
             logger.info(
-                f"process channel ECM from {channel_id},"
-                f" id = {ecm.id}, cmd = {command}"
+                f"process channel ECM from {channel_id}, id = {ecm.id}, cmd = 
{command}"
             )
 
             if command is not None:
@@ -431,7 +430,6 @@ class MainLoop(StoppableQueueBlockingRunnable):
             )
 
     def _send_console_message(self, console_message: ConsoleMessage):
-
         self._async_rpc_client.controller_stub().console_message_triggered(
             ConsoleMessageTriggeredRequest(console_message=console_message)
         )
diff --git a/amber/src/main/python/core/runnables/test_main_loop.py 
b/amber/src/main/python/core/runnables/test_main_loop.py
index 4931389c0b..5ad0afec9b 100644
--- a/amber/src/main/python/core/runnables/test_main_loop.py
+++ b/amber/src/main/python/core/runnables/test_main_loop.py
@@ -338,7 +338,6 @@ class TestMainLoop:
         command_sequence,
         mock_raw_schema,
     ):
-
         operator_code = "from pytexera import *\n" + 
inspect.getsource(EchoOperator)
         command = set_one_of(
             ControlRequest,
@@ -368,7 +367,6 @@ class TestMainLoop:
         command_sequence,
         mock_raw_schema,
     ):
-
         operator_code = "from pytexera import *\n" + inspect.getsource(
             CountBatchOperator
         )
diff --git 
a/amber/src/main/python/core/storage/iceberg/test_iceberg_document.py 
b/amber/src/main/python/core/storage/iceberg/test_iceberg_document.py
index 6d7f7017c6..ad3f49067e 100644
--- a/amber/src/main/python/core/storage/iceberg/test_iceberg_document.py
+++ b/amber/src/main/python/core/storage/iceberg/test_iceberg_document.py
@@ -47,7 +47,6 @@ StorageConfig.initialize(
 
 
 class TestIcebergDocument:
-
     @pytest.fixture
     def amber_schema(self):
         """Sample Amber schema"""
@@ -204,8 +203,7 @@ class TestIcebergDocument:
         # Create writer's batches
         item_batches = [
             all_items[
-                i * batch_size
-                + min(i, remainder) : i * batch_size
+                i * batch_size + min(i, remainder) : i * batch_size
                 + min(i, remainder)
                 + batch_size
                 + (1 if i < remainder else 0)
@@ -213,9 +211,9 @@ class TestIcebergDocument:
             for i in range(num_writers)
         ]
 
-        assert (
-            len(item_batches) == num_writers
-        ), f"Expected {num_writers} batches but got {len(item_batches)}"
+        assert len(item_batches) == num_writers, (
+            f"Expected {num_writers} batches but got {len(item_batches)}"
+        )
 
         # Perform concurrent writes
         def write_batch(batch):
@@ -233,9 +231,9 @@ class TestIcebergDocument:
         # Read all items back
         retrieved_items = list(iceberg_document.get())
         # Verify that the retrieved items match the original items
-        assert set(retrieved_items) == set(
-            all_items
-        ), "All items should be read correctly after concurrent writes."
+        assert set(retrieved_items) == set(all_items), (
+            "All items should be read correctly after concurrent writes."
+        )
 
     def test_read_using_range(self, iceberg_document, sample_items):
         """
@@ -259,14 +257,14 @@ class TestIcebergDocument:
             item for r in ranges for item in 
iceberg_document.get_range(r.start, r.stop)
         ]
 
-        assert len(retrieved_items) == len(
-            sample_items
-        ), "The number of retrieved items does not match the number of all 
items."
+        assert len(retrieved_items) == len(sample_items), (
+            "The number of retrieved items does not match the number of all 
items."
+        )
 
         # Verify that the retrieved items match the original items
-        assert set(retrieved_items) == set(
-            sample_items
-        ), "All items should be retrieved correctly using ranges."
+        assert set(retrieved_items) == set(sample_items), (
+            "All items should be retrieved correctly using ranges."
+        )
 
     def test_get_after(self, iceberg_document, sample_items):
         """
@@ -309,6 +307,6 @@ class TestIcebergDocument:
             writer.put_one(item)
         writer.close()
 
-        assert iceberg_document.get_count() == len(
-            sample_items
-        ), "get_count should return the same number as the length of 
sample_items"
+        assert iceberg_document.get_count() == len(sample_items), (
+            "get_count should return the same number as the length of 
sample_items"
+        )
diff --git a/amber/src/main/python/core/storage/storage_config.py 
b/amber/src/main/python/core/storage/storage_config.py
index 21c918d514..9a03aa713f 100644
--- a/amber/src/main/python/core/storage/storage_config.py
+++ b/amber/src/main/python/core/storage/storage_config.py
@@ -44,7 +44,7 @@ class StorageConfig:
     ):
         if cls._initialized:
             raise RuntimeError(
-                "Storage config has already been initialized" "and cannot be 
modified."
+                "Storage config has already been initializedand cannot be 
modified."
             )
 
         cls.ICEBERG_POSTGRES_CATALOG_URI_WITHOUT_SCHEME = 
postgres_uri_without_scheme
diff --git a/amber/src/main/python/pyproject.toml 
b/amber/src/main/python/pyproject.toml
index ad05ddfba5..72bfeb5724 100644
--- a/amber/src/main/python/pyproject.toml
+++ b/amber/src/main/python/pyproject.toml
@@ -15,17 +15,13 @@
 # specific language governing permissions and limitations
 # under the License.
 
-[tool.black]
+[tool.ruff]
 line-length = 88
-target-version = ["py39", "py310", "py311", "py312"]
-extend-exclude = '''
-/(
-  proto
-)/
-'''
+target-version = "py310"
+extend-exclude = ["proto"]
 
-[tool.flake8]
-extend-ignore = ["F403", "F405", "E203"]
-exclude = ["proto"]
-max-complexity = 10
-max-line-length = 88
\ No newline at end of file
+[tool.ruff.lint]
+ignore = ["F403", "F405", "E203"]
+
+[tool.ruff.lint.mccabe]
+max-complexity = 10
\ No newline at end of file
diff --git a/amber/src/main/python/pytexera/storage/dataset_file_document.py 
b/amber/src/main/python/pytexera/storage/dataset_file_document.py
index 5f3434811b..3d07773583 100644
--- a/amber/src/main/python/pytexera/storage/dataset_file_document.py
+++ b/amber/src/main/python/pytexera/storage/dataset_file_document.py
@@ -73,8 +73,7 @@ class DatasetFileDocument:
 
         if response.status_code != 200:
             raise RuntimeError(
-                f"Failed to get presigned URL: "
-                f"{response.status_code} {response.text}"
+                f"Failed to get presigned URL: {response.status_code} 
{response.text}"
             )
 
         return response.json().get("presignedUrl")
diff --git a/bin/fix-format.sh b/bin/fix-format.sh
index f9b256a9ab..bcaa90f1d5 100755
--- a/bin/fix-format.sh
+++ b/bin/fix-format.sh
@@ -95,14 +95,14 @@ fi
 
 # --- 3) Python formatting ---
 if $run_python; then
-  tx_info "Running black in amber/src/main/python..."
-  if ! command -v black >/dev/null 2>&1; then
-    tx_error "black not found. Install with: pip install black"
+  tx_info "Running ruff in amber/src/main/python..."
+  if ! command -v ruff >/dev/null 2>&1; then
+    tx_error "ruff not found. Install with: pip install ruff"
     exit 1
   fi
   (
     cd "$AMBER_PY_DIR"
-    black .
+    ruff format .
   )
   tx_success "Python formatting completed."
 fi

Reply via email to