diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index b7e43404b86bd3..c6294655f91b4b 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -19,7 +19,7 @@ ci:
     skip: [pylint, pyright, mypy]
 repos:
 -   repo: https://github.com/astral-sh/ruff-pre-commit
-    rev: v0.1.6
+    rev: v0.2.0
     hooks:
     -   id: ruff
         args: [--exit-non-zero-on-fix]
@@ -31,10 +31,8 @@ repos:
         exclude: ^pandas/tests
         args: [--select, "ANN001,ANN2", --fix-only, --exit-non-zero-on-fix]
     -   id: ruff-format
-        # TODO: "." not needed in ruff 0.1.8
-        args: ["."]
 -   repo: https://github.com/jendrikseipp/vulture
-    rev: 'v2.10'
+    rev: v2.11
     hooks:
       - id: vulture
         entry: python scripts/run_vulture.py
diff --git a/pandas/core/apply.py b/pandas/core/apply.py
index 7ae65ba11a752b..512a983486c284 100644
--- a/pandas/core/apply.py
+++ b/pandas/core/apply.py
@@ -1794,7 +1794,7 @@ def normalize_keyword_aggregation(
 
 
 def _make_unique_kwarg_list(
-    seq: Sequence[tuple[Any, Any]]
+    seq: Sequence[tuple[Any, Any]],
 ) -> Sequence[tuple[Any, Any]]:
     """
     Uniquify aggfunc name of the pairs in the order list
diff --git a/pandas/io/parsers/c_parser_wrapper.py b/pandas/io/parsers/c_parser_wrapper.py
index f24d7a628998ee..67f3e5a9f48809 100644
--- a/pandas/io/parsers/c_parser_wrapper.py
+++ b/pandas/io/parsers/c_parser_wrapper.py
@@ -396,7 +396,7 @@ def _concatenate_chunks(chunks: list[dict[int, ArrayLike]]) -> dict:
 
 
 def ensure_dtype_objs(
-    dtype: DtypeArg | dict[Hashable, DtypeArg] | None
+    dtype: DtypeArg | dict[Hashable, DtypeArg] | None,
 ) -> DtypeObj | dict[Hashable, DtypeObj] | None:
     """
     Ensure we have either None, a dtype object, or a dictionary mapping to
diff --git a/pandas/plotting/_matplotlib/core.py b/pandas/plotting/_matplotlib/core.py
index 6fa75ba5fb12d9..1c8cd9a4970c88 100644
--- a/pandas/plotting/_matplotlib/core.py
+++ b/pandas/plotting/_matplotlib/core.py
@@ -465,7 +465,7 @@ def _validate_color_args(self, color, colormap):
     @final
     @staticmethod
     def _iter_data(
-        data: DataFrame | dict[Hashable, Series | DataFrame]
+        data: DataFrame | dict[Hashable, Series | DataFrame],
     ) -> Iterator[tuple[Hashable, np.ndarray]]:
         for col, values in data.items():
             # This was originally written to use values.values before EAs
diff --git a/pandas/plotting/_matplotlib/tools.py b/pandas/plotting/_matplotlib/tools.py
index 89a8a7cf79719e..ff25f0ae21c541 100644
--- a/pandas/plotting/_matplotlib/tools.py
+++ b/pandas/plotting/_matplotlib/tools.py
@@ -98,13 +98,15 @@ def _get_layout(
         nrows, ncols = layout
 
         if nrows == -1 and ncols > 0:
-            layout = nrows, ncols = (ceil(nplots / ncols), ncols)
+            layout = (ceil(nplots / ncols), ncols)
         elif ncols == -1 and nrows > 0:
-            layout = nrows, ncols = (nrows, ceil(nplots / nrows))
+            layout = (nrows, ceil(nplots / nrows))
         elif ncols <= 0 and nrows <= 0:
             msg = "At least one dimension of layout must be positive"
             raise ValueError(msg)
 
+        nrows, ncols = layout
+
         if nrows * ncols < nplots:
             raise ValueError(
                 f"Layout of {nrows}x{ncols} must be larger than required size {nplots}"
diff --git a/pandas/tests/indexes/multi/test_join.py b/pandas/tests/indexes/multi/test_join.py
index 85f15795cdfb51..2be6bba475af7a 100644
--- a/pandas/tests/indexes/multi/test_join.py
+++ b/pandas/tests/indexes/multi/test_join.py
@@ -260,7 +260,7 @@ def test_join_dtypes_all_nan(any_numeric_ea_dtype):
 
 def test_join_index_levels():
     # GH#53093
-    midx = midx = MultiIndex.from_tuples([("a", "2019-02-01"), ("a", "2019-02-01")])
+    midx = MultiIndex.from_tuples([("a", "2019-02-01"), ("a", "2019-02-01")])
     midx2 = MultiIndex.from_tuples([("a", "2019-01-31")])
     result = midx.join(midx2, how="outer")
     expected = MultiIndex.from_tuples(
diff --git a/pyproject.toml b/pyproject.toml
index a7cb87bbca4b72..2039c89af7a9dd 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -187,6 +187,8 @@ environment = {CFLAGS="-g0"}
 line-length = 88
 target-version = "py310"
 fix = true
+
+[tool.ruff.lint]
 unfixable = []
 typing-modules = ["pandas._typing"]
 
@@ -275,6 +277,9 @@ ignore = [
   "PYI024",
   # No builtin `eval()` allowed
   "PGH001",
+  # "S307", # flake8-bandit is not enabled yet
+  # compare-to-empty-string
+  "PLC1901",
   # while int | float can be shortened to float, the former is more explicit
   "PYI041",
   # incorrect-dict-iterator, flags valid Series.items usage
@@ -315,7 +320,7 @@ ignore = [
   # pairwise-over-zipped (>=PY310 only)
   "RUF007",
   # mutable-class-default
-  "RUF012"
+  "RUF012",
 ]
 
 exclude = [
@@ -334,6 +339,7 @@ exclude = [
 "urllib.request.urlopen".msg = "Use pandas.io.common.urlopen instead of urllib.request.urlopen"
 
 [tool.ruff.per-file-ignores]
+[tool.ruff.lint.per-file-ignores]
 # relative imports allowed for asv_bench
 "asv_bench/*" = ["TID", "NPY002"]
 # to be enabled gradually
diff --git a/scripts/validate_min_versions_in_sync.py b/scripts/validate_min_versions_in_sync.py
index f8cce255f532d0..14227730d74ce6 100755
--- a/scripts/validate_min_versions_in_sync.py
+++ b/scripts/validate_min_versions_in_sync.py
@@ -105,7 +105,7 @@ def get_operator_from(dependency: str) -> str | None:
 
 
 def get_yaml_map_from(
-    yaml_dic: list[str | dict[str, list[str]]]
+    yaml_dic: list[str | dict[str, list[str]]],
 ) -> dict[str, list[str] | None]:
     yaml_map: dict[str, list[str] | None] = {}
     for dependency in yaml_dic: