diff --git a/.amazonq/rules/problem-creation.md b/.amazonq/rules/problem-creation.md
index a262244..9249475 100644
--- a/.amazonq/rules/problem-creation.md
+++ b/.amazonq/rules/problem-creation.md
@@ -13,10 +13,11 @@ When user requests a problem by **number** or **name/slug**, the assistant will:
- Images provide crucial visual context, especially for tree and graph problems
- Always verify images are included in `readme_examples` and accessible
4. **Create** JSON file in `leetcode_py/cli/resources/leetcode/json/problems/{problem_name}.json`
-5. **Update** Makefile with `PROBLEM ?= {problem_name}`
-6. **Generate** problem structure using `make p-gen`
-7. **Verify** with `make p-lint` - fix template issues in JSON if possible, or manually fix generated files if template limitations
-8. **Iterate** if JSON fixes: re-run `make p-gen PROBLEM={problem_name} FORCE=1` and `make p-lint` until passes to ensure reproducibility
+5. **Update tags.json5** - If user specifies tags, manually add problem name to corresponding tag arrays in `leetcode_py/cli/resources/leetcode/json/tags.json5`
+6. **Update** Makefile with `PROBLEM ?= {problem_name}`
+7. **Generate** problem structure using `make p-gen`
+8. **Verify** with `make p-lint` - fix template issues in JSON if possible, or manually fix generated files if template limitations
+9. **Iterate** if JSON fixes: re-run `make p-gen PROBLEM={problem_name} FORCE=1` and `make p-lint` until passes to ensure reproducibility
## Scraping Commands
diff --git a/.github/workflows/ci-test.yml b/.github/workflows/ci-test.yml
index 6365946..7116769 100644
--- a/.github/workflows/ci-test.yml
+++ b/.github/workflows/ci-test.yml
@@ -6,9 +6,16 @@ on:
pull_request:
types: [opened, synchronize, reopened]
+env:
+ TARGET_PYTHON_VERSION: "3.13"
+
jobs:
test:
runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ python-version: ["3.10", "3.11", "3.12", "3.13"]
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
@@ -16,7 +23,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
with:
- python-version: "3.13"
+ python-version: ${{ matrix.python-version }}
- name: Install Poetry
uses: snok/install-poetry@76e04a911780d5b312d89783f7b1cd627778900a # v1.4.1
@@ -30,7 +37,7 @@ jobs:
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4
with:
path: .venv
- key: venv-${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('**/poetry.lock') }}
+ key: venv-${{ runner.os }}-${{ matrix.python-version }}-${{ hashFiles('**/poetry.lock') }}
- name: Install dependencies
run: |
@@ -67,11 +74,13 @@ jobs:
run: make test
- name: SonarQube Scan
+ if: matrix.python-version == env.TARGET_PYTHON_VERSION
uses: SonarSource/sonarqube-scan-action@1a6d90ebcb0e6a6b1d87e37ba693fe453195ae25 # v5.3.1
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
- name: Upload coverage reports to Codecov
+ if: matrix.python-version == env.TARGET_PYTHON_VERSION
uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 # v5.5.1
with:
fail_ci_if_error: true
diff --git a/Makefile b/Makefile
index 1849e62..83be5a8 100644
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,5 @@
PYTHON_VERSION = 3.13
-PROBLEM ?= find_all_anagrams_in_a_string
+PROBLEM ?= daily_temperatures
FORCE ?= 0
COMMA := ,
@@ -42,6 +42,7 @@ define lint_target
endef
lint:
+ poetry run python scripts/sort_tags.py
poetry sort
npx prettier --write "**/*.{ts,tsx,css,json,yaml,yml,md}"
$(call lint_target,.)
diff --git a/leetcode/accounts_merge/test_solution.py b/leetcode/accounts_merge/test_solution.py
index 8f18c66..0667cab 100644
--- a/leetcode/accounts_merge/test_solution.py
+++ b/leetcode/accounts_merge/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_accounts_merge, run_accounts_merge
from .solution import Solution
diff --git a/leetcode/add_binary/test_solution.py b/leetcode/add_binary/test_solution.py
index 585a246..bfdf6a7 100644
--- a/leetcode/add_binary/test_solution.py
+++ b/leetcode/add_binary/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_add_binary, run_add_binary
from .solution import Solution
diff --git a/leetcode/balanced_binary_tree/test_solution.py b/leetcode/balanced_binary_tree/test_solution.py
index 97000d1..9d7a061 100644
--- a/leetcode/balanced_binary_tree/test_solution.py
+++ b/leetcode/balanced_binary_tree/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_is_balanced, run_is_balanced
from .solution import Solution
diff --git a/leetcode/basic_calculator/test_solution.py b/leetcode/basic_calculator/test_solution.py
index 8a67479..6bbc871 100644
--- a/leetcode/basic_calculator/test_solution.py
+++ b/leetcode/basic_calculator/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_calculate, run_calculate
from .solution import Solution
diff --git a/leetcode/best_time_to_buy_and_sell_stock/test_solution.py b/leetcode/best_time_to_buy_and_sell_stock/test_solution.py
index 4ed9be5..4100e9f 100644
--- a/leetcode/best_time_to_buy_and_sell_stock/test_solution.py
+++ b/leetcode/best_time_to_buy_and_sell_stock/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_max_profit, run_max_profit
from .solution import Solution
diff --git a/leetcode/binary_search/test_solution.py b/leetcode/binary_search/test_solution.py
index 5ed0cee..f56195c 100644
--- a/leetcode/binary_search/test_solution.py
+++ b/leetcode/binary_search/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_search, run_search
from .solution import Solution
diff --git a/leetcode/binary_tree_level_order_traversal/test_solution.py b/leetcode/binary_tree_level_order_traversal/test_solution.py
index ccb041a..ee7f511 100644
--- a/leetcode/binary_tree_level_order_traversal/test_solution.py
+++ b/leetcode/binary_tree_level_order_traversal/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_level_order, run_level_order
from .solution import Solution
diff --git a/leetcode/binary_tree_right_side_view/test_solution.py b/leetcode/binary_tree_right_side_view/test_solution.py
index 871ecef..260e71b 100644
--- a/leetcode/binary_tree_right_side_view/test_solution.py
+++ b/leetcode/binary_tree_right_side_view/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_right_side_view, run_right_side_view
from .solution import Solution, SolutionBFS, SolutionDFS
diff --git a/leetcode/climbing_stairs/test_solution.py b/leetcode/climbing_stairs/test_solution.py
index a141b4e..a41eff7 100644
--- a/leetcode/climbing_stairs/test_solution.py
+++ b/leetcode/climbing_stairs/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_climb_stairs, run_climb_stairs
from .solution import Solution
diff --git a/leetcode/clone_graph/test_solution.py b/leetcode/clone_graph/test_solution.py
index 95bb87f..4bc8818 100644
--- a/leetcode/clone_graph/test_solution.py
+++ b/leetcode/clone_graph/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_clone_graph, run_clone_graph
from .solution import Solution, SolutionBFS, SolutionDFS
diff --git a/leetcode/coin_change/test_solution.py b/leetcode/coin_change/test_solution.py
index 62eede4..b5013d4 100644
--- a/leetcode/coin_change/test_solution.py
+++ b/leetcode/coin_change/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_coin_change, run_coin_change
from .solution import Solution
diff --git a/leetcode/combination_sum/test_solution.py b/leetcode/combination_sum/test_solution.py
index de34df9..e168a02 100644
--- a/leetcode/combination_sum/test_solution.py
+++ b/leetcode/combination_sum/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_combination_sum, run_combination_sum
from .solution import Solution
diff --git a/leetcode/construct_binary_tree_from_preorder_and_inorder_traversal/test_solution.py b/leetcode/construct_binary_tree_from_preorder_and_inorder_traversal/test_solution.py
index 5f08fe6..88f7534 100644
--- a/leetcode/construct_binary_tree_from_preorder_and_inorder_traversal/test_solution.py
+++ b/leetcode/construct_binary_tree_from_preorder_and_inorder_traversal/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_build_tree, run_build_tree
from .solution import Solution
diff --git a/leetcode/container_with_most_water/test_solution.py b/leetcode/container_with_most_water/test_solution.py
index c2bf0e1..edee77b 100644
--- a/leetcode/container_with_most_water/test_solution.py
+++ b/leetcode/container_with_most_water/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_max_area, run_max_area
from .solution import Solution
diff --git a/leetcode/contains_duplicate/test_solution.py b/leetcode/contains_duplicate/test_solution.py
index e8c2c7e..02178b9 100644
--- a/leetcode/contains_duplicate/test_solution.py
+++ b/leetcode/contains_duplicate/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_contains_duplicate, run_contains_duplicate
from .solution import Solution
diff --git a/leetcode/course_schedule/test_solution.py b/leetcode/course_schedule/test_solution.py
index 35aae35..85e5235 100644
--- a/leetcode/course_schedule/test_solution.py
+++ b/leetcode/course_schedule/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_can_finish, run_can_finish
from .solution import Solution
diff --git a/leetcode/daily_temperatures/README.md b/leetcode/daily_temperatures/README.md
new file mode 100644
index 0000000..ce4e11f
--- /dev/null
+++ b/leetcode/daily_temperatures/README.md
@@ -0,0 +1,46 @@
+# Daily Temperatures
+
+**Difficulty:** Medium
+**Topics:** Array, Stack, Monotonic Stack
+**Tags:** grind
+
+**LeetCode:** [Problem 739](https://leetcode.com/problems/daily-temperatures/description/)
+
+## Problem Description
+
+Given an array of integers `temperatures` represents the daily temperatures, return an array `answer` such that `answer[i]` is the number of days you have to wait after the `ith` day to get a warmer temperature. If there is no future day for which this is possible, keep `answer[i] == 0` instead.
+
+## Examples
+
+### Example 1:
+
+```
+Input: temperatures = [73,74,75,71,69,72,76,73]
+Output: [1,1,4,2,1,1,0,0]
+```
+
+**Explanation:**
+
+- For input `[73,74,75,71,69,72,76,73]`, the output should be `[1,1,4,2,1,1,0,0]`.
+- For example, the first temperature is 73. The next warmer temperature is 74, which is 1 day later, so we put 1.
+- The second temperature is 74. The next warmer temperature is 75, which is 1 day later, so we put 1.
+- The third temperature is 75. The next warmer temperature is 76, which is 4 days later, so we put 4.
+
+### Example 2:
+
+```
+Input: temperatures = [30,40,50,60]
+Output: [1,1,1,0]
+```
+
+### Example 3:
+
+```
+Input: temperatures = [30,60,90]
+Output: [1,1,0]
+```
+
+## Constraints
+
+- `1 <= temperatures.length <= 10^5`
+- `30 <= temperatures[i] <= 100`
diff --git a/leetcode/daily_temperatures/__init__.py b/leetcode/daily_temperatures/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/leetcode/daily_temperatures/helpers.py b/leetcode/daily_temperatures/helpers.py
new file mode 100644
index 0000000..dfef547
--- /dev/null
+++ b/leetcode/daily_temperatures/helpers.py
@@ -0,0 +1,8 @@
+def run_daily_temperatures(solution_class: type, temperatures: list[int]):
+ implementation = solution_class()
+ return implementation.daily_temperatures(temperatures)
+
+
+def assert_daily_temperatures(result: list[int], expected: list[int]) -> bool:
+ assert result == expected
+ return True
diff --git a/leetcode/daily_temperatures/playground.py b/leetcode/daily_temperatures/playground.py
new file mode 100644
index 0000000..18c07bf
--- /dev/null
+++ b/leetcode/daily_temperatures/playground.py
@@ -0,0 +1,29 @@
+# ---
+# jupyter:
+# jupytext:
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_daily_temperatures, run_daily_temperatures
+from solution import Solution
+
+# %%
+# Example test case
+temperatures = [73, 74, 75, 71, 69, 72, 76, 73]
+expected = [1, 1, 4, 2, 1, 1, 0, 0]
+
+# %%
+result = run_daily_temperatures(Solution, temperatures)
+result
+
+# %%
+assert_daily_temperatures(result, expected)
diff --git a/leetcode/daily_temperatures/solution.py b/leetcode/daily_temperatures/solution.py
new file mode 100644
index 0000000..93002d9
--- /dev/null
+++ b/leetcode/daily_temperatures/solution.py
@@ -0,0 +1,15 @@
+class Solution:
+
+ # Time: O(n)
+ # Space: O(n)
+ def daily_temperatures(self, temperatures: list[int]) -> list[int]:
+ result = [0] * len(temperatures)
+ stack: list[int] = []
+
+ for i, temp in enumerate(temperatures):
+ while stack and temperatures[stack[-1]] < temp:
+ prev_index = stack.pop()
+ result[prev_index] = i - prev_index
+ stack.append(i)
+
+ return result
diff --git a/leetcode/daily_temperatures/test_solution.py b/leetcode/daily_temperatures/test_solution.py
new file mode 100644
index 0000000..3a50fc7
--- /dev/null
+++ b/leetcode/daily_temperatures/test_solution.py
@@ -0,0 +1,56 @@
+import pytest
+
+from leetcode_py import logged_test
+
+from .helpers import assert_daily_temperatures, run_daily_temperatures
+from .solution import Solution
+
+
+class TestDailyTemperatures:
+ def setup_method(self):
+ self.solution = Solution()
+
+ @logged_test
+ @pytest.mark.parametrize(
+ "temperatures, expected",
+ [
+ ([73, 74, 75, 71, 69, 72, 76, 73], [1, 1, 4, 2, 1, 1, 0, 0]),
+ ([30, 40, 50, 60], [1, 1, 1, 0]),
+ ([30, 60, 90], [1, 1, 0]),
+ ([89, 62, 70, 58, 47, 47, 46, 76, 100, 70], [8, 1, 5, 4, 3, 2, 1, 1, 0, 0]),
+ ([55, 38, 53, 81, 61, 93, 97, 32, 43, 78], [3, 1, 1, 2, 1, 1, 0, 1, 1, 0]),
+ ([34, 80, 80, 34, 34, 80, 80, 80, 80, 34], [1, 0, 0, 2, 1, 0, 0, 0, 0, 0]),
+ ([73], [0]),
+ ([100], [0]),
+ ([30, 31, 32, 33, 34], [1, 1, 1, 1, 0]),
+ ([90, 80, 70, 60, 50], [0, 0, 0, 0, 0]),
+ ([50, 50, 50, 50], [0, 0, 0, 0]),
+ ([30, 100, 30, 100], [1, 0, 1, 0]),
+ ([75, 71, 69, 72, 76], [4, 2, 1, 1, 0]),
+ ([40, 35, 32, 37, 50], [4, 2, 1, 1, 0]),
+ ([30, 40, 50, 60, 70, 80, 90, 100], [1, 1, 1, 1, 1, 1, 1, 0]),
+ ([30, 30], [0, 0]),
+ ([100, 100], [0, 0]),
+ ([30, 100], [1, 0]),
+ ([100, 30], [0, 0]),
+ ([30, 31, 100], [1, 1, 0]),
+ ([30, 99, 100], [1, 1, 0]),
+ ([50, 40, 60, 30, 70], [2, 1, 2, 1, 0]),
+ ([60, 50, 70, 40, 80], [2, 1, 2, 1, 0]),
+ ([40, 50, 60, 50, 40], [1, 1, 0, 0, 0]),
+ ([30, 40, 50, 40, 30], [1, 1, 0, 0, 0]),
+ ([60, 50, 40, 50, 60], [0, 3, 1, 1, 0]),
+ ([70, 60, 50, 60, 70], [0, 3, 1, 1, 0]),
+ ([45, 50, 40, 60, 55, 65], [1, 2, 1, 2, 1, 0]),
+ ([35, 45, 30, 50, 40, 60], [1, 2, 1, 2, 1, 0]),
+ ([30, 31, 32, 33, 34, 35, 36, 37, 38, 39], [1, 1, 1, 1, 1, 1, 1, 1, 1, 0]),
+ ([50, 49, 48, 47, 46, 45, 44, 43, 42, 41], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]),
+ ([40, 40, 50, 50, 60, 60], [2, 1, 2, 1, 0, 0]),
+ ([60, 60, 50, 50, 40, 40], [0, 0, 0, 0, 0, 0]),
+ ([30, 31, 30, 32], [1, 2, 1, 0]),
+ ([99, 100, 99, 100], [1, 0, 1, 0]),
+ ],
+ )
+ def test_daily_temperatures(self, temperatures: list[int], expected: list[int]):
+ result = run_daily_temperatures(Solution, temperatures)
+ assert_daily_temperatures(result, expected)
diff --git a/leetcode/diagonal_traverse/test_solution.py b/leetcode/diagonal_traverse/test_solution.py
index a57c110..2cec862 100644
--- a/leetcode/diagonal_traverse/test_solution.py
+++ b/leetcode/diagonal_traverse/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_find_diagonal_order, run_find_diagonal_order
from .solution import Solution, SolutionRowShift
diff --git a/leetcode/diameter_of_binary_tree/test_solution.py b/leetcode/diameter_of_binary_tree/test_solution.py
index 11468a0..8a3bffc 100644
--- a/leetcode/diameter_of_binary_tree/test_solution.py
+++ b/leetcode/diameter_of_binary_tree/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_diameter_of_binary_tree, run_diameter_of_binary_tree
from .solution import Solution
diff --git a/leetcode/evaluate_reverse_polish_notation/test_solution.py b/leetcode/evaluate_reverse_polish_notation/test_solution.py
index 244730f..e11cc5b 100644
--- a/leetcode/evaluate_reverse_polish_notation/test_solution.py
+++ b/leetcode/evaluate_reverse_polish_notation/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_eval_rpn, run_eval_rpn
from .solution import Solution
diff --git a/leetcode/find_all_anagrams_in_a_string/test_solution.py b/leetcode/find_all_anagrams_in_a_string/test_solution.py
index 3782888..d16ca19 100644
--- a/leetcode/find_all_anagrams_in_a_string/test_solution.py
+++ b/leetcode/find_all_anagrams_in_a_string/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_find_anagrams, run_find_anagrams
from .solution import Solution
diff --git a/leetcode/find_median_from_data_stream/test_solution.py b/leetcode/find_median_from_data_stream/test_solution.py
index 25ec573..823bd07 100644
--- a/leetcode/find_median_from_data_stream/test_solution.py
+++ b/leetcode/find_median_from_data_stream/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_median_finder, run_median_finder
from .solution import MedianFinder, MedianFinderHybrid
diff --git a/leetcode/first_bad_version/test_solution.py b/leetcode/first_bad_version/test_solution.py
index 3ea5446..916fb2c 100644
--- a/leetcode/first_bad_version/test_solution.py
+++ b/leetcode/first_bad_version/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_first_bad_version, run_first_bad_version
from .solution import Solution
diff --git a/leetcode/flood_fill/test_solution.py b/leetcode/flood_fill/test_solution.py
index c9703c3..cf1b8ab 100644
--- a/leetcode/flood_fill/test_solution.py
+++ b/leetcode/flood_fill/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_flood_fill, run_flood_fill
from .solution import Solution
diff --git a/leetcode/implement_queue_using_stacks/test_solution.py b/leetcode/implement_queue_using_stacks/test_solution.py
index 24520ea..8094d30 100644
--- a/leetcode/implement_queue_using_stacks/test_solution.py
+++ b/leetcode/implement_queue_using_stacks/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_my_queue, run_my_queue
from .solution import MyQueue
diff --git a/leetcode/implement_trie_prefix_tree/test_solution.py b/leetcode/implement_trie_prefix_tree/test_solution.py
index 272e9bd..8a53b20 100644
--- a/leetcode/implement_trie_prefix_tree/test_solution.py
+++ b/leetcode/implement_trie_prefix_tree/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_trie_operations, run_trie_operations
from .solution import Trie
diff --git a/leetcode/insert_interval/test_solution.py b/leetcode/insert_interval/test_solution.py
index a7ab73d..0901983 100644
--- a/leetcode/insert_interval/test_solution.py
+++ b/leetcode/insert_interval/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_insert, run_insert
from .solution import Solution
diff --git a/leetcode/invert_binary_tree/test_solution.py b/leetcode/invert_binary_tree/test_solution.py
index 9e40afb..c86cc48 100644
--- a/leetcode/invert_binary_tree/test_solution.py
+++ b/leetcode/invert_binary_tree/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_invert_tree, run_invert_tree
from .solution import Solution, SolutionBFS, SolutionDFS
diff --git a/leetcode/k_closest_points_to_origin/test_solution.py b/leetcode/k_closest_points_to_origin/test_solution.py
index df391c0..7faa99d 100644
--- a/leetcode/k_closest_points_to_origin/test_solution.py
+++ b/leetcode/k_closest_points_to_origin/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_k_closest, run_k_closest
from .solution import Solution
diff --git a/leetcode/kth_smallest_element_in_a_bst/test_solution.py b/leetcode/kth_smallest_element_in_a_bst/test_solution.py
index be3683e..f12abdb 100644
--- a/leetcode/kth_smallest_element_in_a_bst/test_solution.py
+++ b/leetcode/kth_smallest_element_in_a_bst/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_kth_smallest, run_kth_smallest
from .solution import Solution
diff --git a/leetcode/largest_rectangle_in_histogram/test_solution.py b/leetcode/largest_rectangle_in_histogram/test_solution.py
index 2ac5ee6..66965c5 100644
--- a/leetcode/largest_rectangle_in_histogram/test_solution.py
+++ b/leetcode/largest_rectangle_in_histogram/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_largest_rectangle_area, run_largest_rectangle_area
from .solution import Solution
diff --git a/leetcode/letter_combinations_of_a_phone_number/test_solution.py b/leetcode/letter_combinations_of_a_phone_number/test_solution.py
index c28498c..28c1e2b 100644
--- a/leetcode/letter_combinations_of_a_phone_number/test_solution.py
+++ b/leetcode/letter_combinations_of_a_phone_number/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_letter_combinations, run_letter_combinations
from .solution import Solution
diff --git a/leetcode/linked_list_cycle/test_solution.py b/leetcode/linked_list_cycle/test_solution.py
index 25f3780..c302c6d 100644
--- a/leetcode/linked_list_cycle/test_solution.py
+++ b/leetcode/linked_list_cycle/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_has_cycle, run_has_cycle
from .solution import Solution
diff --git a/leetcode/longest_palindrome/test_solution.py b/leetcode/longest_palindrome/test_solution.py
index 5c8e279..dfdfcce 100644
--- a/leetcode/longest_palindrome/test_solution.py
+++ b/leetcode/longest_palindrome/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_longest_palindrome, run_longest_palindrome
from .solution import Solution
diff --git a/leetcode/longest_palindromic_substring/test_solution.py b/leetcode/longest_palindromic_substring/test_solution.py
index 0cd1994..5751e25 100644
--- a/leetcode/longest_palindromic_substring/test_solution.py
+++ b/leetcode/longest_palindromic_substring/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_longest_palindrome, run_longest_palindrome
from .solution import Solution, SolutionManacher
diff --git a/leetcode/longest_substring_without_repeating_characters/test_solution.py b/leetcode/longest_substring_without_repeating_characters/test_solution.py
index 3193ea9..289a132 100644
--- a/leetcode/longest_substring_without_repeating_characters/test_solution.py
+++ b/leetcode/longest_substring_without_repeating_characters/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_length_of_longest_substring, run_length_of_longest_substring
from .solution import Solution
diff --git a/leetcode/lowest_common_ancestor_of_a_binary_search_tree/test_solution.py b/leetcode/lowest_common_ancestor_of_a_binary_search_tree/test_solution.py
index fd2cede..47dc07b 100644
--- a/leetcode/lowest_common_ancestor_of_a_binary_search_tree/test_solution.py
+++ b/leetcode/lowest_common_ancestor_of_a_binary_search_tree/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_lowest_common_ancestor, run_lowest_common_ancestor
from .solution import Solution
diff --git a/leetcode/lowest_common_ancestor_of_a_binary_tree/test_solution.py b/leetcode/lowest_common_ancestor_of_a_binary_tree/test_solution.py
index 3d28a96..6b01c21 100644
--- a/leetcode/lowest_common_ancestor_of_a_binary_tree/test_solution.py
+++ b/leetcode/lowest_common_ancestor_of_a_binary_tree/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_lowest_common_ancestor, run_lowest_common_ancestor
from .solution import Solution
diff --git a/leetcode/lru_cache/test_solution.py b/leetcode/lru_cache/test_solution.py
index db70723..3a3c15f 100644
--- a/leetcode/lru_cache/test_solution.py
+++ b/leetcode/lru_cache/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_lru_cache, run_lru_cache
from .solution import LRUCache, LRUCacheWithDoublyList
diff --git a/leetcode/majority_element/test_solution.py b/leetcode/majority_element/test_solution.py
index ef73961..eaa2b55 100644
--- a/leetcode/majority_element/test_solution.py
+++ b/leetcode/majority_element/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_majority_element, run_majority_element
from .solution import Solution
diff --git a/leetcode/maximum_depth_of_binary_tree/test_solution.py b/leetcode/maximum_depth_of_binary_tree/test_solution.py
index e86979c..285a10a 100644
--- a/leetcode/maximum_depth_of_binary_tree/test_solution.py
+++ b/leetcode/maximum_depth_of_binary_tree/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_max_depth, run_max_depth
from .solution import Solution
diff --git a/leetcode/maximum_profit_in_job_scheduling/test_solution.py b/leetcode/maximum_profit_in_job_scheduling/test_solution.py
index 8e17451..ec132a6 100644
--- a/leetcode/maximum_profit_in_job_scheduling/test_solution.py
+++ b/leetcode/maximum_profit_in_job_scheduling/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_job_scheduling, run_job_scheduling
from .solution import Solution
diff --git a/leetcode/maximum_subarray/test_solution.py b/leetcode/maximum_subarray/test_solution.py
index ddb4d89..841b14b 100644
--- a/leetcode/maximum_subarray/test_solution.py
+++ b/leetcode/maximum_subarray/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_max_sub_array, run_max_sub_array
from .solution import Solution
diff --git a/leetcode/merge_intervals/test_solution.py b/leetcode/merge_intervals/test_solution.py
index 195cced..b8d86f6 100644
--- a/leetcode/merge_intervals/test_solution.py
+++ b/leetcode/merge_intervals/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_merge, run_merge
from .solution import Solution
diff --git a/leetcode/merge_k_sorted_lists/test_solution.py b/leetcode/merge_k_sorted_lists/test_solution.py
index e9d9cfc..15d491c 100644
--- a/leetcode/merge_k_sorted_lists/test_solution.py
+++ b/leetcode/merge_k_sorted_lists/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_merge_k_lists, run_merge_k_lists
from .solution import Solution
diff --git a/leetcode/merge_two_sorted_lists/test_solution.py b/leetcode/merge_two_sorted_lists/test_solution.py
index 58b2887..1f04409 100644
--- a/leetcode/merge_two_sorted_lists/test_solution.py
+++ b/leetcode/merge_two_sorted_lists/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_merge_two_lists, run_merge_two_lists
from .solution import Solution
diff --git a/leetcode/middle_of_the_linked_list/test_solution.py b/leetcode/middle_of_the_linked_list/test_solution.py
index 0cdfdd0..ae85cfb 100644
--- a/leetcode/middle_of_the_linked_list/test_solution.py
+++ b/leetcode/middle_of_the_linked_list/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_middle_node, run_middle_node
from .solution import Solution
diff --git a/leetcode/min_stack/test_solution.py b/leetcode/min_stack/test_solution.py
index fd75d51..4e781e2 100644
--- a/leetcode/min_stack/test_solution.py
+++ b/leetcode/min_stack/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_min_stack_operations, run_min_stack_operations
from .solution import MinStack
diff --git a/leetcode/minimum_height_trees/test_solution.py b/leetcode/minimum_height_trees/test_solution.py
index 74cd63c..b03370a 100644
--- a/leetcode/minimum_height_trees/test_solution.py
+++ b/leetcode/minimum_height_trees/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_find_min_height_trees, run_find_min_height_trees
from .solution import Solution
diff --git a/leetcode/minimum_window_substring/test_solution.py b/leetcode/minimum_window_substring/test_solution.py
index a76fe7a..2e33210 100644
--- a/leetcode/minimum_window_substring/test_solution.py
+++ b/leetcode/minimum_window_substring/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_min_window, run_min_window
from .solution import Solution
diff --git a/leetcode/number_of_islands/test_solution.py b/leetcode/number_of_islands/test_solution.py
index 2b37d2d..44a247e 100644
--- a/leetcode/number_of_islands/test_solution.py
+++ b/leetcode/number_of_islands/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_num_islands, run_num_islands
from .solution import Solution
diff --git a/leetcode/partition_equal_subset_sum/test_solution.py b/leetcode/partition_equal_subset_sum/test_solution.py
index a480df5..0a983dc 100644
--- a/leetcode/partition_equal_subset_sum/test_solution.py
+++ b/leetcode/partition_equal_subset_sum/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_can_partition, run_can_partition
from .solution import Solution, SolutionBitset
diff --git a/leetcode/permutations/test_solution.py b/leetcode/permutations/test_solution.py
index d8d1bce..92237b9 100644
--- a/leetcode/permutations/test_solution.py
+++ b/leetcode/permutations/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_permute, run_permute
from .solution import Solution
diff --git a/leetcode/product_of_array_except_self/test_solution.py b/leetcode/product_of_array_except_self/test_solution.py
index 84013ac..fe69a3c 100644
--- a/leetcode/product_of_array_except_self/test_solution.py
+++ b/leetcode/product_of_array_except_self/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_product_except_self, run_product_except_self
from .solution import Solution
diff --git a/leetcode/ransom_note/test_solution.py b/leetcode/ransom_note/test_solution.py
index 429913a..d5613e0 100644
--- a/leetcode/ransom_note/test_solution.py
+++ b/leetcode/ransom_note/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_can_construct, run_can_construct
from .solution import Solution
diff --git a/leetcode/reverse_linked_list/test_solution.py b/leetcode/reverse_linked_list/test_solution.py
index 8f8198e..8a8c4d0 100644
--- a/leetcode/reverse_linked_list/test_solution.py
+++ b/leetcode/reverse_linked_list/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_reverse_list, run_reverse_list
from .solution import Solution
diff --git a/leetcode/reverse_linked_list_ii/test_solution.py b/leetcode/reverse_linked_list_ii/test_solution.py
index 5964711..56770a1 100644
--- a/leetcode/reverse_linked_list_ii/test_solution.py
+++ b/leetcode/reverse_linked_list_ii/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_reverse_between, run_reverse_between
from .solution import Solution
diff --git a/leetcode/rotting_oranges/test_solution.py b/leetcode/rotting_oranges/test_solution.py
index 9710fb8..571852a 100644
--- a/leetcode/rotting_oranges/test_solution.py
+++ b/leetcode/rotting_oranges/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_oranges_rotting, run_oranges_rotting
from .solution import Solution
diff --git a/leetcode/search_in_rotated_sorted_array/test_solution.py b/leetcode/search_in_rotated_sorted_array/test_solution.py
index a227711..29303eb 100644
--- a/leetcode/search_in_rotated_sorted_array/test_solution.py
+++ b/leetcode/search_in_rotated_sorted_array/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_search, run_search
from .solution import Solution
diff --git a/leetcode/serialize_and_deserialize_binary_tree/test_solution.py b/leetcode/serialize_and_deserialize_binary_tree/test_solution.py
index 8f67b6e..056ca2f 100644
--- a/leetcode/serialize_and_deserialize_binary_tree/test_solution.py
+++ b/leetcode/serialize_and_deserialize_binary_tree/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_serialize_deserialize, run_serialize_deserialize
from .solution import Codec
diff --git a/leetcode/sort_colors/test_solution.py b/leetcode/sort_colors/test_solution.py
index 43f6085..a9aaa03 100644
--- a/leetcode/sort_colors/test_solution.py
+++ b/leetcode/sort_colors/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_sort_colors, run_sort_colors
from .solution import Solution
diff --git a/leetcode/spiral_matrix/test_solution.py b/leetcode/spiral_matrix/test_solution.py
index 22bda84..d79d8ab 100644
--- a/leetcode/spiral_matrix/test_solution.py
+++ b/leetcode/spiral_matrix/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_spiral_order, run_spiral_order
from .solution import Solution
diff --git a/leetcode/string_to_integer_atoi/test_solution.py b/leetcode/string_to_integer_atoi/test_solution.py
index d09898c..e73c34a 100644
--- a/leetcode/string_to_integer_atoi/test_solution.py
+++ b/leetcode/string_to_integer_atoi/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_my_atoi, run_my_atoi
from .solution import Solution
diff --git a/leetcode/subsets/test_solution.py b/leetcode/subsets/test_solution.py
index 6efd136..e16ab8c 100644
--- a/leetcode/subsets/test_solution.py
+++ b/leetcode/subsets/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_subsets, run_subsets
from .solution import Solution
diff --git a/leetcode/task_scheduler/test_solution.py b/leetcode/task_scheduler/test_solution.py
index 85794ce..441fa58 100644
--- a/leetcode/task_scheduler/test_solution.py
+++ b/leetcode/task_scheduler/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_least_interval, run_least_interval
from .solution import Solution
diff --git a/leetcode/three_sum/test_solution.py b/leetcode/three_sum/test_solution.py
index 2bba573..46d0263 100644
--- a/leetcode/three_sum/test_solution.py
+++ b/leetcode/three_sum/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_three_sum, run_three_sum
from .solution import Solution
diff --git a/leetcode/time_based_key_value_store/test_solution.py b/leetcode/time_based_key_value_store/test_solution.py
index c0c16ff..fb955f7 100644
--- a/leetcode/time_based_key_value_store/test_solution.py
+++ b/leetcode/time_based_key_value_store/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_time_map_operations, run_time_map_operations
from .solution import TimeMap
diff --git a/leetcode/trapping_rain_water/test_solution.py b/leetcode/trapping_rain_water/test_solution.py
index c35bb3f..ab8cd90 100644
--- a/leetcode/trapping_rain_water/test_solution.py
+++ b/leetcode/trapping_rain_water/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_trap, run_trap
from .solution import Solution
diff --git a/leetcode/two_sum/test_solution.py b/leetcode/two_sum/test_solution.py
index 75cb3f3..a251146 100644
--- a/leetcode/two_sum/test_solution.py
+++ b/leetcode/two_sum/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_two_sum, run_two_sum
from .solution import Solution
diff --git a/leetcode/unique_paths/test_solution.py b/leetcode/unique_paths/test_solution.py
index b66ad67..1e9be3c 100644
--- a/leetcode/unique_paths/test_solution.py
+++ b/leetcode/unique_paths/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_unique_paths, run_unique_paths
from .solution import Solution, SolutionMath
diff --git a/leetcode/valid_anagram/test_solution.py b/leetcode/valid_anagram/test_solution.py
index 8a46646..0acd896 100644
--- a/leetcode/valid_anagram/test_solution.py
+++ b/leetcode/valid_anagram/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_is_anagram, run_is_anagram
from .solution import Solution
diff --git a/leetcode/valid_palindrome/test_solution.py b/leetcode/valid_palindrome/test_solution.py
index 83b4362..7804ca5 100644
--- a/leetcode/valid_palindrome/test_solution.py
+++ b/leetcode/valid_palindrome/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_is_palindrome, run_is_palindrome
from .solution import Solution
diff --git a/leetcode/valid_parentheses/test_solution.py b/leetcode/valid_parentheses/test_solution.py
index 7e4c8b2..78f33d3 100644
--- a/leetcode/valid_parentheses/test_solution.py
+++ b/leetcode/valid_parentheses/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_is_valid, run_is_valid
from .solution import Solution
diff --git a/leetcode/validate_binary_search_tree/test_solution.py b/leetcode/validate_binary_search_tree/test_solution.py
index 2491911..e71dd32 100644
--- a/leetcode/validate_binary_search_tree/test_solution.py
+++ b/leetcode/validate_binary_search_tree/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_is_valid_bst, run_is_valid_bst
from .solution import Solution, SolutionBFS, SolutionDFS
diff --git a/leetcode/word_break/test_solution.py b/leetcode/word_break/test_solution.py
index 97a2619..7463d00 100644
--- a/leetcode/word_break/test_solution.py
+++ b/leetcode/word_break/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_word_break, run_word_break
from .solution import Solution
diff --git a/leetcode/word_ladder/test_solution.py b/leetcode/word_ladder/test_solution.py
index 94008a8..2239150 100644
--- a/leetcode/word_ladder/test_solution.py
+++ b/leetcode/word_ladder/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_ladder_length, run_ladder_length
from .solution import Solution
diff --git a/leetcode/word_search/test_solution.py b/leetcode/word_search/test_solution.py
index 5e1151d..32f041c 100644
--- a/leetcode/word_search/test_solution.py
+++ b/leetcode/word_search/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_exist, run_exist
from .solution import Solution
diff --git a/leetcode/zero_one_matrix/test_solution.py b/leetcode/zero_one_matrix/test_solution.py
index 4129b47..d7823c0 100644
--- a/leetcode/zero_one_matrix/test_solution.py
+++ b/leetcode/zero_one_matrix/test_solution.py
@@ -1,6 +1,6 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
from .helpers import assert_update_matrix, run_update_matrix
from .solution import Solution
diff --git a/leetcode_py/__init__.py b/leetcode_py/__init__.py
index 0300fc0..8134f11 100644
--- a/leetcode_py/__init__.py
+++ b/leetcode_py/__init__.py
@@ -1,5 +1,6 @@
from leetcode_py.data_structures.graph_node import GraphNode
from leetcode_py.data_structures.list_node import ListNode
from leetcode_py.data_structures.tree_node import TreeNode
+from leetcode_py.tools.logged_test import logged_test
-__all__ = ["GraphNode", "ListNode", "TreeNode"]
+__all__ = ["GraphNode", "ListNode", "TreeNode", "logged_test"]
diff --git a/leetcode_py/cli/resources/leetcode/cookiecutter.json b/leetcode_py/cli/resources/leetcode/cookiecutter.json
index 4287ea1..e8175f0 100644
--- a/leetcode_py/cli/resources/leetcode/cookiecutter.json
+++ b/leetcode_py/cli/resources/leetcode/cookiecutter.json
@@ -18,7 +18,7 @@
"readme_constraints": "- 2 <= nums.length <= 10^4\n- -10^9 <= nums[i] <= 10^9\n- -10^9 <= target <= 10^9\n- Only one valid answer exists.",
"readme_additional": "",
- "helpers_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .solution import Solution",
+ "helpers_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .solution import Solution",
"helpers_content": "",
"helpers_run_name": "two_sum",
"helpers_run_signature": "(solution_class: type, nums: list[int], target: int)",
@@ -31,7 +31,7 @@
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_two_sum, run_two_sum\nfrom .solution import Solution",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_two_sum, run_two_sum\nfrom .solution import Solution",
"test_content": "",
"test_class_name": "TwoSum",
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
diff --git a/leetcode_py/cli/resources/leetcode/examples/example.json5 b/leetcode_py/cli/resources/leetcode/examples/example.json5
index 09c1c24..57d4159 100644
--- a/leetcode_py/cli/resources/leetcode/examples/example.json5
+++ b/leetcode_py/cli/resources/leetcode/examples/example.json5
@@ -1,196 +1,200 @@
{
- // ============================================================================
- // COMPREHENSIVE LEETCODE TEMPLATE EXAMPLE
- // ============================================================================
- // This example demonstrates ALL template patterns using valid_anagram as base
- // with comprehensive comments showing variations for different problem types.
- //
- // REFERENCE PROBLEMS (see .templates/leetcode/json/ for complete examples):
- // 1. valid_anagram - Basic: string parameters, boolean return
- // 2. invert_binary_tree - Tree: TreeNode imports/parameters
- // 3. merge_two_sorted_lists - LinkedList: ListNode imports/parameters
- // 4. lru_cache - Design: custom class, multiple methods, operations
- // 5. implement_trie_prefix_tree - Trie: DictTree inheritance
- // ============================================================================
+ // ============================================================================
+ // COMPREHENSIVE LEETCODE TEMPLATE EXAMPLE
+ // ============================================================================
+ // This example demonstrates ALL template patterns using valid_anagram as base
+ // with comprehensive comments showing variations for different problem types.
+ //
+ // REFERENCE PROBLEMS (see .templates/leetcode/json/ for complete examples):
+ // 1. valid_anagram - Basic: string parameters, boolean return
+ // 2. invert_binary_tree - Tree: TreeNode imports/parameters
+ // 3. merge_two_sorted_lists - LinkedList: ListNode imports/parameters
+ // 4. lru_cache - Design: custom class, multiple methods, operations
+ // 5. implement_trie_prefix_tree - Trie: DictTree inheritance
+ // ============================================================================
- // === PROBLEM IDENTIFICATION ===
- "problem_name": "valid_anagram", // snake_case: used for directory/file names
- "solution_class_name": "Solution", // "Solution" for basic problems
- // "LRUCache" for design problems
- // "Trie(DictTree[str])" for inheritance
- "problem_number": "242", // LeetCode problem number as string
- "problem_title": "Valid Anagram", // Exact title from LeetCode
- "difficulty": "Easy", // Easy, Medium, Hard
- "topics": "Hash Table, String, Sorting", // Comma-separated topics from LeetCode
- "_tags": { "list": ["grind-75"] }, // Optional: common problem set tags
- // Use _tags wrapper for cookiecutter lists
+ // === PROBLEM IDENTIFICATION ===
+ problem_name: "valid_anagram", // snake_case: used for directory/file names
+ solution_class_name: "Solution", // "Solution" for basic problems
+ // "LRUCache" for design problems
+ // "Trie(DictTree[str])" for inheritance
+ problem_number: "242", // LeetCode problem number as string
+ problem_title: "Valid Anagram", // Exact title from LeetCode
+ difficulty: "Easy", // Easy, Medium, Hard
+ topics: "Hash Table, String, Sorting", // Comma-separated topics from LeetCode
+ _tags: { list: ["grind-75"] }, // Optional: common problem set tags
+ // Use _tags wrapper for cookiecutter lists
- // === README CONTENT ===
- // IMPORTANT: Preserve rich HTML content from LeetCode including:
- // - Code snippets with backticks: `code`
- // - Bold text: **bold** or bold
- // - Italic text: *italic* or italic
- // - Images: 
- // - HTML formatting:
,
,
, - , etc.
- // - Mathematical expressions and special characters
- "readme_description": "Given two strings `s` and `t`, return `true` if `t` is an anagram of `s`, and `false` otherwise.",
+ // === README CONTENT ===
+ // IMPORTANT: Preserve rich HTML content from LeetCode including:
+ // - Code snippets with backticks: `code`
+ // - Bold text: **bold** or bold
+ // - Italic text: *italic* or italic
+ // - Images: 
+ // - HTML formatting:
,
,
, - , etc.
+ // - Mathematical expressions and special characters
+ readme_description: "Given two strings `s` and `t`, return `true` if `t` is an anagram of `s`, and `false` otherwise.",
- "_readme_examples": { // Use _readme_examples wrapper for cookiecutter lists
- "list": [
- { "content": "```\nInput: s = \"anagram\", t = \"nagaram\"\nOutput: true\n```" },
- { "content": "```\nInput: s = \"rat\", t = \"car\"\nOutput: false\n```" }
- // For tree problems: Include images
- // { "content": "\n\n```\nInput: root = [4,2,7,1,3,6,9]\nOutput: [4,7,2,9,6,3,1]\n```" }
- ]
- },
+ _readme_examples: {
+ // Use _readme_examples wrapper for cookiecutter lists
+ list: [
+ { content: '```\nInput: s = "anagram", t = "nagaram"\nOutput: true\n```' },
+ { content: '```\nInput: s = "rat", t = "car"\nOutput: false\n```' },
+ // For tree problems: Include images
+ // { "content": "\n\n```\nInput: root = [4,2,7,1,3,6,9]\nOutput: [4,7,2,9,6,3,1]\n```" }
+ ],
+ },
- "readme_constraints": "- 1 <= s.length, t.length <= 5 * 10^4\n- s and t consist of lowercase English letters.",
- "readme_additional": "**Follow up:** What if the inputs contain Unicode characters? How would you adapt your solution to such a case?",
+ readme_constraints: "- 1 <= s.length, t.length <= 5 * 10^4\n- s and t consist of lowercase English letters.",
+ readme_additional: "**Follow up:** What if the inputs contain Unicode characters? How would you adapt your solution to such a case?",
- // === HELPER FUNCTIONS ===
- // New template system uses helper functions for cleaner test organization
- "helpers_imports": "", // Empty for basic problems
- // "from leetcode_py import TreeNode" for tree problems
- // "from leetcode_py import ListNode" for linked list problems
- "helpers_content": "", // Additional helper content if needed
- "helpers_run_name": "is_anagram", // Function name matching main method
- "helpers_run_signature": "(solution_class: type, s: str, t: str)",
- // For tree: "(solution_class: type, root_list: list[int | None])"
- // For linked list: "(solution_class: type, list1_vals: list[int], list2_vals: list[int])"
- // For design: "(solution_class: type, operations: list[str], inputs: list[list[int]])"
- "helpers_run_body": " implementation = solution_class()\n return implementation.is_anagram(s, t)",
- // For tree: " root = TreeNode[int].from_list(root_list)\n implementation = solution_class()\n return implementation.invert_tree(root)"
- // For design: " cache = None\n results: list[int | None] = []\n # ... operation loop ...\n return results, cache"
- "helpers_assert_name": "is_anagram", // Function name matching main method
- "helpers_assert_signature": "(result: bool, expected: bool) -> bool",
- // For tree: "(result: TreeNode[int] | None, expected_list: list[int | None]) -> bool"
- // For design: "(result: list[int | None], expected: list[int | None]) -> bool"
- "helpers_assert_body": " assert result == expected\n return True",
- // For tree: " expected = TreeNode[int].from_list(expected_list)\n assert result == expected\n return True"
+ // === HELPER FUNCTIONS ===
+ // New template system uses helper functions for cleaner test organization
+ helpers_imports: "", // Empty for basic problems
+ // "from leetcode_py import TreeNode" for tree problems
+ // "from leetcode_py import ListNode" for linked list problems
+ helpers_content: "", // Additional helper content if needed
+ helpers_run_name: "is_anagram", // Function name matching main method
+ helpers_run_signature: "(solution_class: type, s: str, t: str)",
+ // For tree: "(solution_class: type, root_list: list[int | None])"
+ // For linked list: "(solution_class: type, list1_vals: list[int], list2_vals: list[int])"
+ // For design: "(solution_class: type, operations: list[str], inputs: list[list[int]])"
+ helpers_run_body: " implementation = solution_class()\n return implementation.is_anagram(s, t)",
+ // For tree: " root = TreeNode[int].from_list(root_list)\n implementation = solution_class()\n return implementation.invert_tree(root)"
+ // For design: " cache = None\n results: list[int | None] = []\n # ... operation loop ...\n return results, cache"
+ helpers_assert_name: "is_anagram", // Function name matching main method
+ helpers_assert_signature: "(result: bool, expected: bool) -> bool",
+ // For tree: "(result: TreeNode[int] | None, expected_list: list[int | None]) -> bool"
+ // For design: "(result: list[int | None], expected: list[int | None]) -> bool"
+ helpers_assert_body: " assert result == expected\n return True",
+ // For tree: " expected = TreeNode[int].from_list(expected_list)\n assert result == expected\n return True"
- // === SOLUTION TEMPLATE ===
- "solution_imports": "", // Empty for basic problems
- // "from leetcode_py import TreeNode" for tree problems
- // "from leetcode_py import ListNode" for linked list problems
- // "from leetcode_py.data_structures import DictTree, RecursiveDict" for trie problems
- "solution_contents": "", // Additional content before class definition
- "solution_class_content": "", // Content inside class definition (usually empty)
+ // === SOLUTION TEMPLATE ===
+ solution_imports: "", // Empty for basic problems
+ // "from leetcode_py import TreeNode" for tree problems
+ // "from leetcode_py import ListNode" for linked list problems
+ // "from leetcode_py.data_structures import DictTree, RecursiveDict" for trie problems
+ solution_contents: "", // Additional content before class definition
+ solution_class_content: "", // Content inside class definition (usually empty)
- // === TEST CONFIGURATION ===
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_is_anagram, run_is_anagram\nfrom .solution import Solution",
- // For design: "from .solution import LRUCache" instead of Solution
- "test_content": "", // Additional test content
- "test_class_name": "ValidAnagram", // PascalCase: TestClassName for pytest class
- "test_class_content": " def setup_method(self):\n self.solution = Solution()",
- // Empty for design problems: ""
+ // === TEST CONFIGURATION ===
+ test_imports: "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_is_anagram, run_is_anagram\nfrom .solution import Solution",
+ // For design: "from .solution import LRUCache" instead of Solution
+ test_content: "", // Additional test content
+ test_class_name: "ValidAnagram", // PascalCase: TestClassName for pytest class
+ test_class_content: " def setup_method(self):\n self.solution = Solution()",
+ // Empty for design problems: ""
- // === SOLUTION METHODS ===
- "_solution_methods": { // Use _solution_methods wrapper for cookiecutter lists
- "list": [
- {
- "name": "is_anagram", // snake_case method name
- "signature": "(self, s: str, t: str) -> bool", // Full method signature with type hints
- // For tree: "(self, root: TreeNode[int] | None) -> TreeNode[int] | None"
- // For linked list: "(self, list1: ListNode[int] | None, list2: ListNode[int] | None) -> ListNode[int] | None"
- "body": " # TODO: Implement is_anagram\n return False"
- // For design problems with __init__:
- // { "name": "__init__", "signature": "(self, capacity: int) -> None", "body": " # TODO: Initialize\n pass" }
- }
- ]
- },
+ // === SOLUTION METHODS ===
+ _solution_methods: {
+ // Use _solution_methods wrapper for cookiecutter lists
+ list: [
+ {
+ name: "is_anagram", // snake_case method name
+ signature: "(self, s: str, t: str) -> bool", // Full method signature with type hints
+ // For tree: "(self, root: TreeNode[int] | None) -> TreeNode[int] | None"
+ // For linked list: "(self, list1: ListNode[int] | None, list2: ListNode[int] | None) -> ListNode[int] | None"
+ body: " # TODO: Implement is_anagram\n return False",
+ // For design problems with __init__:
+ // { "name": "__init__", "signature": "(self, capacity: int) -> None", "body": " # TODO: Initialize\n pass" }
+ },
+ ],
+ },
- // === TEST HELPER METHODS ===
- "_test_helper_methods": { // Use _test_helper_methods wrapper for cookiecutter lists
- "list": [
- { "name": "setup_method", "parameters": "", "body": "self.solution = Solution()" }
- // Empty list for design problems: []
- ]
- },
+ // === TEST HELPER METHODS ===
+ _test_helper_methods: {
+ // Use _test_helper_methods wrapper for cookiecutter lists
+ list: [
+ { name: "setup_method", parameters: "", body: "self.solution = Solution()" },
+ // Empty list for design problems: []
+ ],
+ },
- // === TEST METHODS ===
- "_test_methods": { // Use _test_methods wrapper for cookiecutter lists
- "list": [
- {
- "name": "test_is_anagram", // test_{method_name}
- "signature": "(self, s: str, t: str, expected: bool)", // Method signature with type hints
- "parametrize": "s, t, expected", // pytest parametrize parameters
- // For tree: "root_list, expected_list"
- // For design: "operations, inputs, expected"
- "test_cases": "[('anagram', 'nagaram', True), ('rat', 'car', False), ('listen', 'silent', True), ('hello', 'bello', False), ('', '', True), ('a', 'a', True), ('a', 'b', False), ('ab', 'ba', True), ('abc', 'bca', True), ('abc', 'def', False), ('aab', 'abb', False), ('aabbcc', 'abcabc', True), ('abcd', 'abcde', False), ('race', 'care', True), ('elbow', 'below', True), ('study', 'dusty', True), ('night', 'thing', True), ('stressed', 'desserts', True)]",
- // For tree: "[([4, 2, 7, 1, 3, 6, 9], [4, 7, 2, 9, 6, 3, 1]), ([2, 1, 3], [2, 3, 1]), ([], [])]"
- // For design: "[(['LRUCache', 'put', 'get'], [[2], [1, 1], [1]], [None, None, 1])]"
- "body": " result = run_is_anagram(Solution, s, t)\n assert_is_anagram(result, expected)"
- // For tree: " result = run_invert_tree(Solution, root_list)\n assert_invert_tree(result, expected_list)"
- // For design: " result, _ = run_lru_cache(LRUCache, operations, inputs)\n assert_lru_cache(result, expected)"
- }
- ]
- },
+ // === TEST METHODS ===
+ _test_methods: {
+ // Use _test_methods wrapper for cookiecutter lists
+ list: [
+ {
+ name: "test_is_anagram", // test_{method_name}
+ signature: "(self, s: str, t: str, expected: bool)", // Method signature with type hints
+ parametrize: "s, t, expected", // pytest parametrize parameters
+ // For tree: "root_list, expected_list"
+ // For design: "operations, inputs, expected"
+ test_cases: "[('anagram', 'nagaram', True), ('rat', 'car', False), ('listen', 'silent', True), ('hello', 'bello', False), ('', '', True), ('a', 'a', True), ('a', 'b', False), ('ab', 'ba', True), ('abc', 'bca', True), ('abc', 'def', False), ('aab', 'abb', False), ('aabbcc', 'abcabc', True), ('abcd', 'abcde', False), ('race', 'care', True), ('elbow', 'below', True), ('study', 'dusty', True), ('night', 'thing', True), ('stressed', 'desserts', True)]",
+ // For tree: "[([4, 2, 7, 1, 3, 6, 9], [4, 7, 2, 9, 6, 3, 1]), ([2, 1, 3], [2, 3, 1]), ([], [])]"
+ // For design: "[(['LRUCache', 'put', 'get'], [[2], [1, 1], [1]], [None, None, 1])]"
+ body: " result = run_is_anagram(Solution, s, t)\n assert_is_anagram(result, expected)",
+ // For tree: " result = run_invert_tree(Solution, root_list)\n assert_invert_tree(result, expected_list)"
+ // For design: " result, _ = run_lru_cache(LRUCache, operations, inputs)\n assert_lru_cache(result, expected)"
+ },
+ ],
+ },
- // === PLAYGROUND NOTEBOOK ===
- // CRITICAL: Use single quotes for Python strings to avoid JSON escaping issues with Jupyter notebooks
- // Double quotes in JSON + cookiecutter + Jupyter notebook = triple escaping issues
- // ALWAYS use single quotes: s = 'hello', not s = "hello"
- "playground_imports": "from helpers import run_is_anagram, assert_is_anagram\nfrom solution import Solution",
- // For tree: "from helpers import run_invert_tree, assert_invert_tree\nfrom solution import Solution\nfrom leetcode_py import TreeNode"
- // For design: "from helpers import run_lru_cache, assert_lru_cache\nfrom solution import LRUCache"
- "playground_setup": "# Example test case\ns = 'anagram'\nt = 'nagaram'\nexpected = True",
- // For tree: "# Example test case\nroot_list: list[int | None] = [4, 2, 7, 1, 3, 6, 9]\nexpected_list: list[int | None] = [4, 7, 2, 9, 6, 3, 1]"
- // For design: "# Example test case\noperations = ['LRUCache', 'put', 'get']\ninputs = [[2], [1, 1], [1]]\nexpected = [None, None, 1]"
- "playground_run": "result = run_is_anagram(Solution, s, t)\nresult",
- // For tree: "result = run_invert_tree(Solution, root_list)\nresult"
- // For design: "result, cache = run_lru_cache(LRUCache, operations, inputs)\nprint(result)\ncache"
- "playground_assert": "assert_is_anagram(result, expected)"
- // For tree: "assert_invert_tree(result, expected_list)"
- // For design: "assert_lru_cache(result, expected)"
+ // === PLAYGROUND NOTEBOOK ===
+ // CRITICAL: Use single quotes for Python strings to avoid JSON escaping issues with Jupyter notebooks
+ // Double quotes in JSON + cookiecutter + Jupyter notebook = triple escaping issues
+ // ALWAYS use single quotes: s = 'hello', not s = "hello"
+ playground_imports: "from helpers import run_is_anagram, assert_is_anagram\nfrom solution import Solution",
+ // For tree: "from helpers import run_invert_tree, assert_invert_tree\nfrom solution import Solution\nfrom leetcode_py import TreeNode"
+ // For design: "from helpers import run_lru_cache, assert_lru_cache\nfrom solution import LRUCache"
+ playground_setup: "# Example test case\ns = 'anagram'\nt = 'nagaram'\nexpected = True",
+ // For tree: "# Example test case\nroot_list: list[int | None] = [4, 2, 7, 1, 3, 6, 9]\nexpected_list: list[int | None] = [4, 7, 2, 9, 6, 3, 1]"
+ // For design: "# Example test case\noperations = ['LRUCache', 'put', 'get']\ninputs = [[2], [1, 1], [1]]\nexpected = [None, None, 1]"
+ playground_run: "result = run_is_anagram(Solution, s, t)\nresult",
+ // For tree: "result = run_invert_tree(Solution, root_list)\nresult"
+ // For design: "result, cache = run_lru_cache(LRUCache, operations, inputs)\nprint(result)\ncache"
+ playground_assert: "assert_is_anagram(result, expected)",
+ // For tree: "assert_invert_tree(result, expected_list)"
+ // For design: "assert_lru_cache(result, expected)"
- // ============================================================================
- // PROBLEM TYPE VARIATIONS SUMMARY:
- // ============================================================================
- //
- // BASIC PROBLEMS (valid_anagram):
- // - solution_class_name: "Solution"
- // - solution_imports: ""
- // - Simple method signatures: "(self, s: str, t: str) -> bool"
- // - Basic test cases: string/number parameters
- // - Playground: single quotes for strings
- //
- // TREE PROBLEMS (invert_binary_tree):
- // - solution_class_name: "Solution"
- // - solution_imports: "from leetcode_py import TreeNode"
- // - Tree method signatures: "(self, root: TreeNode[int] | None) -> TreeNode[int] | None"
- // - Helper functions use TreeNode.from_list()
- // - Test cases: list representations of trees
- // - Playground: TreeNode imports and list conversions
- //
- // LINKED LIST PROBLEMS (merge_two_sorted_lists):
- // - solution_class_name: "Solution"
- // - solution_imports: "from leetcode_py import ListNode"
- // - List method signatures: "(self, list1: ListNode[int] | None, list2: ListNode[int] | None) -> ListNode[int] | None"
- // - Helper functions use ListNode.from_list()
- // - Test cases: list representations of linked lists
- // - Playground: ListNode imports and list conversions
- //
- // DESIGN PROBLEMS (lru_cache):
- // - solution_class_name: "LRUCache" (custom class name)
- // - Multiple methods including __init__
- // - Operations-based testing: operations, inputs, expected arrays
- // - Complex test body with operation loops
- // - Helper functions return (results, instance) for debugging
- // - Playground: print results, return instance
- // - test_class_content: "" (no setup_method)
- //
- // INHERITANCE PROBLEMS (implement_trie_prefix_tree):
- // - solution_class_name: "Trie(DictTree[str])" (with inheritance)
- // - solution_imports: "from leetcode_py.data_structures import DictTree, RecursiveDict"
- // - Custom class with inheritance from DictTree
- // - Operations-based testing like design problems
- // - Helper functions return (results, instance) for debugging
- //
- // MULTIPLE SOLUTIONS (invert_binary_tree, lru_cache):
- // - Add parametrize for solution classes in test files:
- // @pytest.mark.parametrize("solution_class", [Solution, SolutionDFS, SolutionBFS])
- // @pytest.mark.parametrize("solution_class", [LRUCache, LRUCacheWithDoublyList])
- // - Update test method signature to include solution_class parameter
- // - Import all solution classes in test file
- // ============================================================================
+ // ============================================================================
+ // PROBLEM TYPE VARIATIONS SUMMARY:
+ // ============================================================================
+ //
+ // BASIC PROBLEMS (valid_anagram):
+ // - solution_class_name: "Solution"
+ // - solution_imports: ""
+ // - Simple method signatures: "(self, s: str, t: str) -> bool"
+ // - Basic test cases: string/number parameters
+ // - Playground: single quotes for strings
+ //
+ // TREE PROBLEMS (invert_binary_tree):
+ // - solution_class_name: "Solution"
+ // - solution_imports: "from leetcode_py import TreeNode"
+ // - Tree method signatures: "(self, root: TreeNode[int] | None) -> TreeNode[int] | None"
+ // - Helper functions use TreeNode.from_list()
+ // - Test cases: list representations of trees
+ // - Playground: TreeNode imports and list conversions
+ //
+ // LINKED LIST PROBLEMS (merge_two_sorted_lists):
+ // - solution_class_name: "Solution"
+ // - solution_imports: "from leetcode_py import ListNode"
+ // - List method signatures: "(self, list1: ListNode[int] | None, list2: ListNode[int] | None) -> ListNode[int] | None"
+ // - Helper functions use ListNode.from_list()
+ // - Test cases: list representations of linked lists
+ // - Playground: ListNode imports and list conversions
+ //
+ // DESIGN PROBLEMS (lru_cache):
+ // - solution_class_name: "LRUCache" (custom class name)
+ // - Multiple methods including __init__
+ // - Operations-based testing: operations, inputs, expected arrays
+ // - Complex test body with operation loops
+ // - Helper functions return (results, instance) for debugging
+ // - Playground: print results, return instance
+ // - test_class_content: "" (no setup_method)
+ //
+ // INHERITANCE PROBLEMS (implement_trie_prefix_tree):
+ // - solution_class_name: "Trie(DictTree[str])" (with inheritance)
+ // - solution_imports: "from leetcode_py.data_structures import DictTree, RecursiveDict"
+ // - Custom class with inheritance from DictTree
+ // - Operations-based testing like design problems
+ // - Helper functions return (results, instance) for debugging
+ //
+ // MULTIPLE SOLUTIONS (invert_binary_tree, lru_cache):
+ // - Add parametrize for solution classes in test files:
+ // @pytest.mark.parametrize("solution_class", [Solution, SolutionDFS, SolutionBFS])
+ // @pytest.mark.parametrize("solution_class", [LRUCache, LRUCacheWithDoublyList])
+ // - Update test method signature to include solution_class parameter
+ // - Import all solution classes in test file
+ // ============================================================================
}
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/accounts_merge.json b/leetcode_py/cli/resources/leetcode/json/problems/accounts_merge.json
index ec92c0d..08b14e0 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/accounts_merge.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/accounts_merge.json
@@ -30,7 +30,7 @@
"solution_imports": "",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_accounts_merge, run_accounts_merge\nfrom .solution import Solution",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_accounts_merge, run_accounts_merge\nfrom .solution import Solution",
"test_content": "",
"test_class_name": "AccountsMerge",
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/add_binary.json b/leetcode_py/cli/resources/leetcode/json/problems/add_binary.json
index 5b2e293..34caf68 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/add_binary.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/add_binary.json
@@ -26,7 +26,7 @@
"solution_imports": "",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_add_binary, run_add_binary\nfrom .solution import Solution",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_add_binary, run_add_binary\nfrom .solution import Solution",
"test_content": "",
"test_class_name": "AddBinary",
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/balanced_binary_tree.json b/leetcode_py/cli/resources/leetcode/json/problems/balanced_binary_tree.json
index c413c49..2d5db13 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/balanced_binary_tree.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/balanced_binary_tree.json
@@ -31,7 +31,7 @@
"solution_imports": "from leetcode_py import TreeNode",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_is_balanced, run_is_balanced\nfrom .solution import Solution",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_is_balanced, run_is_balanced\nfrom .solution import Solution",
"test_content": "",
"test_class_name": "BalancedBinaryTree",
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/basic_calculator.json b/leetcode_py/cli/resources/leetcode/json/problems/basic_calculator.json
index 6644911..74d4066 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/basic_calculator.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/basic_calculator.json
@@ -27,7 +27,7 @@
"solution_imports": "",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_calculate, run_calculate\nfrom .solution import Solution",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_calculate, run_calculate\nfrom .solution import Solution",
"test_content": "",
"test_class_name": "BasicCalculator",
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/best_time_to_buy_and_sell_stock.json b/leetcode_py/cli/resources/leetcode/json/problems/best_time_to_buy_and_sell_stock.json
index 0c86cdb..7e4ca68 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/best_time_to_buy_and_sell_stock.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/best_time_to_buy_and_sell_stock.json
@@ -30,7 +30,7 @@
"solution_imports": "",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_max_profit, run_max_profit\nfrom .solution import Solution",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_max_profit, run_max_profit\nfrom .solution import Solution",
"test_content": "",
"test_class_name": "BestTimeToBuyAndSellStock",
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/binary_search.json b/leetcode_py/cli/resources/leetcode/json/problems/binary_search.json
index 8a15f0d..3166d10 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/binary_search.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/binary_search.json
@@ -30,7 +30,7 @@
"solution_imports": "",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_search, run_search\nfrom .solution import Solution",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_search, run_search\nfrom .solution import Solution",
"test_content": "",
"test_class_name": "BinarySearch",
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/binary_tree_level_order_traversal.json b/leetcode_py/cli/resources/leetcode/json/problems/binary_tree_level_order_traversal.json
index ba006e5..e1cde47 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/binary_tree_level_order_traversal.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/binary_tree_level_order_traversal.json
@@ -29,7 +29,7 @@
"solution_imports": "from leetcode_py import TreeNode",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_level_order, run_level_order\nfrom .solution import Solution",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_level_order, run_level_order\nfrom .solution import Solution",
"test_content": "",
"test_class_name": "BinaryTreeLevelOrderTraversal",
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/binary_tree_right_side_view.json b/leetcode_py/cli/resources/leetcode/json/problems/binary_tree_right_side_view.json
index 2607777..8852d72 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/binary_tree_right_side_view.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/binary_tree_right_side_view.json
@@ -32,7 +32,7 @@
"solution_imports": "from leetcode_py import TreeNode",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_right_side_view, run_right_side_view\nfrom .solution import Solution",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_right_side_view, run_right_side_view\nfrom .solution import Solution",
"test_content": "",
"test_class_name": "BinaryTreeRightSideView",
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/climbing_stairs.json b/leetcode_py/cli/resources/leetcode/json/problems/climbing_stairs.json
index c5a1375..fd3db16 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/climbing_stairs.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/climbing_stairs.json
@@ -30,7 +30,7 @@
"solution_imports": "",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_climb_stairs, run_climb_stairs\nfrom .solution import Solution",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_climb_stairs, run_climb_stairs\nfrom .solution import Solution",
"test_content": "",
"test_class_name": "ClimbingStairs",
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/clone_graph.json b/leetcode_py/cli/resources/leetcode/json/problems/clone_graph.json
index 7165af5..75f6244 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/clone_graph.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/clone_graph.json
@@ -33,7 +33,7 @@
"solution_imports": "from leetcode_py import GraphNode",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_clone_graph, run_clone_graph\nfrom .solution import Solution",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_clone_graph, run_clone_graph\nfrom .solution import Solution",
"test_content": "",
"test_class_name": "CloneGraph",
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/coin_change.json b/leetcode_py/cli/resources/leetcode/json/problems/coin_change.json
index 480f745..a668904 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/coin_change.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/coin_change.json
@@ -29,7 +29,7 @@
"solution_imports": "",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_coin_change, run_coin_change\nfrom .solution import Solution",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_coin_change, run_coin_change\nfrom .solution import Solution",
"test_content": "",
"test_class_name": "CoinChange",
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/combination_sum.json b/leetcode_py/cli/resources/leetcode/json/problems/combination_sum.json
index 44610a1..74786a8 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/combination_sum.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/combination_sum.json
@@ -31,7 +31,7 @@
"solution_imports": "",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_combination_sum, run_combination_sum\nfrom .solution import Solution",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_combination_sum, run_combination_sum\nfrom .solution import Solution",
"test_content": "",
"test_class_name": "CombinationSum",
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/construct_binary_tree_from_preorder_and_inorder_traversal.json b/leetcode_py/cli/resources/leetcode/json/problems/construct_binary_tree_from_preorder_and_inorder_traversal.json
index e7e7716..6546085 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/construct_binary_tree_from_preorder_and_inorder_traversal.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/construct_binary_tree_from_preorder_and_inorder_traversal.json
@@ -28,7 +28,7 @@
"solution_imports": "from leetcode_py import TreeNode",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_build_tree, run_build_tree\nfrom .solution import Solution",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_build_tree, run_build_tree\nfrom .solution import Solution",
"test_content": "",
"test_class_name": "ConstructBinaryTreeFromPreorderAndInorderTraversal",
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/container_with_most_water.json b/leetcode_py/cli/resources/leetcode/json/problems/container_with_most_water.json
index f07668b..f7c23cb 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/container_with_most_water.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/container_with_most_water.json
@@ -28,7 +28,7 @@
"solution_imports": "",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_max_area, run_max_area\nfrom .solution import Solution",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_max_area, run_max_area\nfrom .solution import Solution",
"test_content": "",
"test_class_name": "ContainerWithMostWater",
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/contains_duplicate.json b/leetcode_py/cli/resources/leetcode/json/problems/contains_duplicate.json
index 0601beb..b623280 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/contains_duplicate.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/contains_duplicate.json
@@ -31,7 +31,7 @@
"solution_imports": "",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_contains_duplicate, run_contains_duplicate\nfrom .solution import Solution",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_contains_duplicate, run_contains_duplicate\nfrom .solution import Solution",
"test_content": "",
"test_class_name": "ContainsDuplicate",
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/course_schedule.json b/leetcode_py/cli/resources/leetcode/json/problems/course_schedule.json
index f02e646..44fc4db 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/course_schedule.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/course_schedule.json
@@ -30,7 +30,7 @@
"solution_imports": "",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_can_finish, run_can_finish\nfrom .solution import Solution",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_can_finish, run_can_finish\nfrom .solution import Solution",
"test_content": "",
"test_class_name": "CourseSchedule",
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/daily_temperatures.json b/leetcode_py/cli/resources/leetcode/json/problems/daily_temperatures.json
new file mode 100644
index 0000000..6dc3945
--- /dev/null
+++ b/leetcode_py/cli/resources/leetcode/json/problems/daily_temperatures.json
@@ -0,0 +1,63 @@
+{
+ "problem_name": "daily_temperatures",
+ "solution_class_name": "Solution",
+ "problem_number": "739",
+ "problem_title": "Daily Temperatures",
+ "difficulty": "Medium",
+ "topics": "Array, Stack, Monotonic Stack",
+ "_tags": { "list": [] },
+ "readme_description": "Given an array of integers `temperatures` represents the daily temperatures, return an array `answer` such that `answer[i]` is the number of days you have to wait after the `ith` day to get a warmer temperature. If there is no future day for which this is possible, keep `answer[i] == 0` instead.",
+ "_readme_examples": {
+ "list": [
+ {
+ "content": "```\nInput: temperatures = [73,74,75,71,69,72,76,73]\nOutput: [1,1,4,2,1,1,0,0]\n```\n**Explanation:**\n- For input `[73,74,75,71,69,72,76,73]`, the output should be `[1,1,4,2,1,1,0,0]`.\n- For example, the first temperature is 73. The next warmer temperature is 74, which is 1 day later, so we put 1.\n- The second temperature is 74. The next warmer temperature is 75, which is 1 day later, so we put 1.\n- The third temperature is 75. The next warmer temperature is 76, which is 4 days later, so we put 4."
+ },
+ { "content": "```\nInput: temperatures = [30,40,50,60]\nOutput: [1,1,1,0]\n```" },
+ { "content": "```\nInput: temperatures = [30,60,90]\nOutput: [1,1,0]\n```" }
+ ]
+ },
+ "readme_constraints": "- `1 <= temperatures.length <= 10^5`\n- `30 <= temperatures[i] <= 100`",
+ "readme_additional": "",
+ "helpers_imports": "",
+ "helpers_content": "",
+ "helpers_run_name": "daily_temperatures",
+ "helpers_run_signature": "(solution_class: type, temperatures: list[int])",
+ "helpers_run_body": " implementation = solution_class()\n return implementation.daily_temperatures(temperatures)",
+ "helpers_assert_name": "daily_temperatures",
+ "helpers_assert_signature": "(result: list[int], expected: list[int]) -> bool",
+ "helpers_assert_body": " assert result == expected\n return True",
+ "solution_imports": "",
+ "solution_contents": "",
+ "solution_class_content": "",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_daily_temperatures, run_daily_temperatures\nfrom .solution import Solution",
+ "test_content": "",
+ "test_class_name": "DailyTemperatures",
+ "test_class_content": " def setup_method(self):\n self.solution = Solution()",
+ "_solution_methods": {
+ "list": [
+ {
+ "name": "daily_temperatures",
+ "signature": "(self, temperatures: list[int]) -> list[int]",
+ "body": " # TODO: Implement daily_temperatures\n return []"
+ }
+ ]
+ },
+ "_test_helper_methods": {
+ "list": [{ "name": "setup_method", "parameters": "", "body": "self.solution = Solution()" }]
+ },
+ "_test_methods": {
+ "list": [
+ {
+ "name": "test_daily_temperatures",
+ "signature": "(self, temperatures: list[int], expected: list[int])",
+ "parametrize": "temperatures, expected",
+ "test_cases": "[([73,74,75,71,69,72,76,73], [1,1,4,2,1,1,0,0]), ([30,40,50,60], [1,1,1,0]), ([30,60,90], [1,1,0]), ([89,62,70,58,47,47,46,76,100,70], [8,1,5,4,3,2,1,1,0,0]), ([55,38,53,81,61,93,97,32,43,78], [3,1,1,2,1,1,0,1,1,0]), ([34,80,80,34,34,80,80,80,80,34], [1,0,0,2,1,0,0,0,0,0]), ([73], [0]), ([100], [0]), ([30,31,32,33,34], [1,1,1,1,0]), ([90,80,70,60,50], [0,0,0,0,0]), ([50,50,50,50], [0,0,0,0]), ([30,100,30,100], [1,0,1,0]), ([75,71,69,72,76], [4,2,1,1,0]), ([40,35,32,37,50], [4,2,1,1,0]), ([30,40,50,60,70,80,90,100], [1,1,1,1,1,1,1,0]), ([30,30], [0,0]), ([100,100], [0,0]), ([30,100], [1,0]), ([100,30], [0,0]), ([30,31,100], [1,1,0]), ([30,99,100], [1,1,0]), ([50,40,60,30,70], [2,1,2,1,0]), ([60,50,70,40,80], [2,1,2,1,0]), ([40,50,60,50,40], [1,1,0,0,0]), ([30,40,50,40,30], [1,1,0,0,0]), ([60,50,40,50,60], [0,3,1,1,0]), ([70,60,50,60,70], [0,3,1,1,0]), ([45,50,40,60,55,65], [1,2,1,2,1,0]), ([35,45,30,50,40,60], [1,2,1,2,1,0]), ([30,31,32,33,34,35,36,37,38,39], [1,1,1,1,1,1,1,1,1,0]), ([50,49,48,47,46,45,44,43,42,41], [0,0,0,0,0,0,0,0,0,0]), ([40,40,50,50,60,60], [2,1,2,1,0,0]), ([60,60,50,50,40,40], [0,0,0,0,0,0]), ([30,31,30,32], [1,2,1,0]), ([99,100,99,100], [1,0,1,0])]",
+ "body": " result = run_daily_temperatures(Solution, temperatures)\n assert_daily_temperatures(result, expected)"
+ }
+ ]
+ },
+ "playground_imports": "from helpers import run_daily_temperatures, assert_daily_temperatures\nfrom solution import Solution",
+ "playground_setup": "# Example test case\ntemperatures = [73,74,75,71,69,72,76,73]\nexpected = [1,1,4,2,1,1,0,0]",
+ "playground_run": "result = run_daily_temperatures(Solution, temperatures)\nresult",
+ "playground_assert": "assert_daily_temperatures(result, expected)"
+}
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/diagonal_traverse.json b/leetcode_py/cli/resources/leetcode/json/problems/diagonal_traverse.json
index df17575..6a06a56 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/diagonal_traverse.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/diagonal_traverse.json
@@ -28,7 +28,7 @@
"solution_imports": "",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_find_diagonal_order, run_find_diagonal_order\nfrom .solution import Solution",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_find_diagonal_order, run_find_diagonal_order\nfrom .solution import Solution",
"test_content": "",
"test_class_name": "DiagonalTraverse",
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/diameter_of_binary_tree.json b/leetcode_py/cli/resources/leetcode/json/problems/diameter_of_binary_tree.json
index 0eec11f..2415601 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/diameter_of_binary_tree.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/diameter_of_binary_tree.json
@@ -28,7 +28,7 @@
"solution_imports": "from leetcode_py import TreeNode",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_diameter_of_binary_tree, run_diameter_of_binary_tree\nfrom .solution import Solution",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_diameter_of_binary_tree, run_diameter_of_binary_tree\nfrom .solution import Solution",
"test_content": "",
"test_class_name": "DiameterOfBinaryTree",
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/evaluate_reverse_polish_notation.json b/leetcode_py/cli/resources/leetcode/json/problems/evaluate_reverse_polish_notation.json
index 7d4e83d..b2f5048 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/evaluate_reverse_polish_notation.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/evaluate_reverse_polish_notation.json
@@ -33,7 +33,7 @@
"solution_imports": "",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_eval_rpn, run_eval_rpn\nfrom .solution import Solution",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_eval_rpn, run_eval_rpn\nfrom .solution import Solution",
"test_content": "",
"test_class_name": "EvaluateReversePolishNotation",
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/find_all_anagrams_in_a_string.json b/leetcode_py/cli/resources/leetcode/json/problems/find_all_anagrams_in_a_string.json
index fa9cd1a..8eba9a3 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/find_all_anagrams_in_a_string.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/find_all_anagrams_in_a_string.json
@@ -30,7 +30,7 @@
"solution_imports": "",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_find_anagrams, run_find_anagrams\nfrom .solution import Solution",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_find_anagrams, run_find_anagrams\nfrom .solution import Solution",
"test_content": "",
"test_class_name": "FindAllAnagramsInAString",
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/find_median_from_data_stream.json b/leetcode_py/cli/resources/leetcode/json/problems/find_median_from_data_stream.json
index 3a85fa4..11b2961 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/find_median_from_data_stream.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/find_median_from_data_stream.json
@@ -27,7 +27,7 @@
"solution_imports": "",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_median_finder, run_median_finder\nfrom .solution import MedianFinder",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_median_finder, run_median_finder\nfrom .solution import MedianFinder",
"test_content": "",
"test_class_name": "FindMedianFromDataStream",
"test_class_content": "",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/first_bad_version.json b/leetcode_py/cli/resources/leetcode/json/problems/first_bad_version.json
index 0b30e0b..d8b8699 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/first_bad_version.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/first_bad_version.json
@@ -28,7 +28,7 @@
"solution_imports": "",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_first_bad_version, run_first_bad_version\nfrom .solution import Solution",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_first_bad_version, run_first_bad_version\nfrom .solution import Solution",
"test_content": "",
"test_class_name": "FirstBadVersion",
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/flood_fill.json b/leetcode_py/cli/resources/leetcode/json/problems/flood_fill.json
index a889a3d..b63f274 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/flood_fill.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/flood_fill.json
@@ -30,7 +30,7 @@
"solution_imports": "",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_flood_fill, run_flood_fill\nfrom .solution import Solution",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_flood_fill, run_flood_fill\nfrom .solution import Solution",
"test_content": "",
"test_class_name": "FloodFill",
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/implement_queue_using_stacks.json b/leetcode_py/cli/resources/leetcode/json/problems/implement_queue_using_stacks.json
index f2adeb5..bb35f25 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/implement_queue_using_stacks.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/implement_queue_using_stacks.json
@@ -27,7 +27,7 @@
"solution_imports": "",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_my_queue, run_my_queue\nfrom .solution import MyQueue",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_my_queue, run_my_queue\nfrom .solution import MyQueue",
"test_content": "",
"test_class_name": "ImplementQueueUsingStacks",
"test_class_content": "",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/implement_trie_prefix_tree.json b/leetcode_py/cli/resources/leetcode/json/problems/implement_trie_prefix_tree.json
index c800080..c4af8b9 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/implement_trie_prefix_tree.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/implement_trie_prefix_tree.json
@@ -27,7 +27,7 @@
"solution_imports": "from leetcode_py.data_structures import DictTree, RecursiveDict",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_trie_operations, run_trie_operations\nfrom .solution import Trie",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_trie_operations, run_trie_operations\nfrom .solution import Trie",
"test_content": "",
"test_class_name": "ImplementTriePrefixTree",
"test_class_content": "",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/insert_interval.json b/leetcode_py/cli/resources/leetcode/json/problems/insert_interval.json
index 44280d1..5429660 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/insert_interval.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/insert_interval.json
@@ -30,7 +30,7 @@
"solution_imports": "",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_insert, run_insert\nfrom .solution import Solution",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_insert, run_insert\nfrom .solution import Solution",
"test_content": "",
"test_class_name": "InsertInterval",
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/invert_binary_tree.json b/leetcode_py/cli/resources/leetcode/json/problems/invert_binary_tree.json
index b0d5f66..4d8af88 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/invert_binary_tree.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/invert_binary_tree.json
@@ -27,7 +27,7 @@
"solution_imports": "from leetcode_py import TreeNode",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_invert_tree, run_invert_tree\nfrom .solution import Solution",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_invert_tree, run_invert_tree\nfrom .solution import Solution",
"test_content": "",
"test_class_name": "InvertBinaryTree",
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/k_closest_points_to_origin.json b/leetcode_py/cli/resources/leetcode/json/problems/k_closest_points_to_origin.json
index d5ebb8f..4c0523b 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/k_closest_points_to_origin.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/k_closest_points_to_origin.json
@@ -30,7 +30,7 @@
"solution_imports": "",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_k_closest, run_k_closest\nfrom .solution import Solution",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_k_closest, run_k_closest\nfrom .solution import Solution",
"test_content": "",
"test_class_name": "KClosestPointsToOrigin",
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/kth_smallest_element_in_a_bst.json b/leetcode_py/cli/resources/leetcode/json/problems/kth_smallest_element_in_a_bst.json
index 0b4f072..0c10237 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/kth_smallest_element_in_a_bst.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/kth_smallest_element_in_a_bst.json
@@ -30,7 +30,7 @@
"solution_imports": "from leetcode_py import TreeNode",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_kth_smallest, run_kth_smallest\nfrom .solution import Solution",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_kth_smallest, run_kth_smallest\nfrom .solution import Solution",
"test_content": "",
"test_class_name": "KthSmallestElementInABst",
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/largest_rectangle_in_histogram.json b/leetcode_py/cli/resources/leetcode/json/problems/largest_rectangle_in_histogram.json
index 2fe5780..e76b075 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/largest_rectangle_in_histogram.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/largest_rectangle_in_histogram.json
@@ -30,7 +30,7 @@
"solution_imports": "",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_largest_rectangle_area, run_largest_rectangle_area\nfrom .solution import Solution",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_largest_rectangle_area, run_largest_rectangle_area\nfrom .solution import Solution",
"test_content": "",
"test_class_name": "LargestRectangleInHistogram",
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/letter_combinations_of_a_phone_number.json b/leetcode_py/cli/resources/leetcode/json/problems/letter_combinations_of_a_phone_number.json
index 990afaf..0fb1f5f 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/letter_combinations_of_a_phone_number.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/letter_combinations_of_a_phone_number.json
@@ -29,7 +29,7 @@
"solution_imports": "",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_letter_combinations, run_letter_combinations\nfrom .solution import Solution",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_letter_combinations, run_letter_combinations\nfrom .solution import Solution",
"test_content": "",
"test_class_name": "LetterCombinationsOfAPhoneNumber",
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/linked_list_cycle.json b/leetcode_py/cli/resources/leetcode/json/problems/linked_list_cycle.json
index 53aefe2..be053cb 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/linked_list_cycle.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/linked_list_cycle.json
@@ -33,7 +33,7 @@
"solution_imports": "from leetcode_py import ListNode",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_has_cycle, run_has_cycle\nfrom .solution import Solution",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_has_cycle, run_has_cycle\nfrom .solution import Solution",
"test_content": "",
"test_class_name": "LinkedListCycle",
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/longest_palindrome.json b/leetcode_py/cli/resources/leetcode/json/problems/longest_palindrome.json
index 8208c5d..0307637 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/longest_palindrome.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/longest_palindrome.json
@@ -30,7 +30,7 @@
"solution_imports": "",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_longest_palindrome, run_longest_palindrome\nfrom .solution import Solution",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_longest_palindrome, run_longest_palindrome\nfrom .solution import Solution",
"test_content": "",
"test_class_name": "LongestPalindrome",
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/longest_palindromic_substring.json b/leetcode_py/cli/resources/leetcode/json/problems/longest_palindromic_substring.json
index de0e3fc..81a4263 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/longest_palindromic_substring.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/longest_palindromic_substring.json
@@ -28,7 +28,7 @@
"solution_imports": "",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_longest_palindrome, run_longest_palindrome\nfrom .solution import Solution",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_longest_palindrome, run_longest_palindrome\nfrom .solution import Solution",
"test_content": "",
"test_class_name": "LongestPalindromicSubstring",
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/longest_substring_without_repeating_characters.json b/leetcode_py/cli/resources/leetcode/json/problems/longest_substring_without_repeating_characters.json
index 50c04c2..5c4c6b0 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/longest_substring_without_repeating_characters.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/longest_substring_without_repeating_characters.json
@@ -33,7 +33,7 @@
"solution_imports": "",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_length_of_longest_substring, run_length_of_longest_substring\nfrom .solution import Solution",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_length_of_longest_substring, run_length_of_longest_substring\nfrom .solution import Solution",
"test_content": "",
"test_class_name": "LongestSubstringWithoutRepeatingCharacters",
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/lowest_common_ancestor_of_a_binary_search_tree.json b/leetcode_py/cli/resources/leetcode/json/problems/lowest_common_ancestor_of_a_binary_search_tree.json
index e310ef7..3eb7da0 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/lowest_common_ancestor_of_a_binary_search_tree.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/lowest_common_ancestor_of_a_binary_search_tree.json
@@ -31,7 +31,7 @@
"solution_imports": "from leetcode_py import TreeNode",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_lowest_common_ancestor, run_lowest_common_ancestor\nfrom .solution import Solution",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_lowest_common_ancestor, run_lowest_common_ancestor\nfrom .solution import Solution",
"test_content": "",
"test_class_name": "LowestCommonAncestorOfABinarySearchTree",
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/lowest_common_ancestor_of_a_binary_tree.json b/leetcode_py/cli/resources/leetcode/json/problems/lowest_common_ancestor_of_a_binary_tree.json
index 44c77e2..f5f5e36 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/lowest_common_ancestor_of_a_binary_tree.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/lowest_common_ancestor_of_a_binary_tree.json
@@ -31,7 +31,7 @@
"solution_imports": "from leetcode_py import TreeNode",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_lowest_common_ancestor, run_lowest_common_ancestor\nfrom .solution import Solution",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_lowest_common_ancestor, run_lowest_common_ancestor\nfrom .solution import Solution",
"test_content": "",
"test_class_name": "LowestCommonAncestorOfABinaryTree",
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/lru_cache.json b/leetcode_py/cli/resources/leetcode/json/problems/lru_cache.json
index 2765e1f..2ecd230 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/lru_cache.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/lru_cache.json
@@ -27,7 +27,7 @@
"solution_imports": "",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_lru_cache, run_lru_cache\nfrom .solution import LRUCache",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_lru_cache, run_lru_cache\nfrom .solution import LRUCache",
"test_content": "",
"test_class_name": "LRUCache",
"test_class_content": "",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/majority_element.json b/leetcode_py/cli/resources/leetcode/json/problems/majority_element.json
index 36f438e..01c8606 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/majority_element.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/majority_element.json
@@ -26,7 +26,7 @@
"solution_imports": "",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_majority_element, run_majority_element\nfrom .solution import Solution",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_majority_element, run_majority_element\nfrom .solution import Solution",
"test_content": "",
"test_class_name": "MajorityElement",
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/maximum_depth_of_binary_tree.json b/leetcode_py/cli/resources/leetcode/json/problems/maximum_depth_of_binary_tree.json
index 1d62f00..217a983 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/maximum_depth_of_binary_tree.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/maximum_depth_of_binary_tree.json
@@ -28,7 +28,7 @@
"solution_imports": "from leetcode_py import TreeNode",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_max_depth, run_max_depth\nfrom .solution import Solution",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_max_depth, run_max_depth\nfrom .solution import Solution",
"test_content": "",
"test_class_name": "MaximumDepthOfBinaryTree",
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/maximum_profit_in_job_scheduling.json b/leetcode_py/cli/resources/leetcode/json/problems/maximum_profit_in_job_scheduling.json
index 54c0cd2..4a2f47d 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/maximum_profit_in_job_scheduling.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/maximum_profit_in_job_scheduling.json
@@ -33,7 +33,7 @@
"solution_imports": "",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_job_scheduling, run_job_scheduling\nfrom .solution import Solution",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_job_scheduling, run_job_scheduling\nfrom .solution import Solution",
"test_content": "",
"test_class_name": "MaximumProfitInJobScheduling",
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/maximum_subarray.json b/leetcode_py/cli/resources/leetcode/json/problems/maximum_subarray.json
index 8c9bd1b..9e0611a 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/maximum_subarray.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/maximum_subarray.json
@@ -33,7 +33,7 @@
"solution_imports": "",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_max_sub_array, run_max_sub_array\nfrom .solution import Solution",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_max_sub_array, run_max_sub_array\nfrom .solution import Solution",
"test_content": "",
"test_class_name": "MaximumSubarray",
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/merge_intervals.json b/leetcode_py/cli/resources/leetcode/json/problems/merge_intervals.json
index 606741c..244f3e7 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/merge_intervals.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/merge_intervals.json
@@ -33,7 +33,7 @@
"solution_imports": "",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_merge, run_merge\nfrom .solution import Solution",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_merge, run_merge\nfrom .solution import Solution",
"test_content": "",
"test_class_name": "MergeIntervals",
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/merge_k_sorted_lists.json b/leetcode_py/cli/resources/leetcode/json/problems/merge_k_sorted_lists.json
index d85adc6..4c58547 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/merge_k_sorted_lists.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/merge_k_sorted_lists.json
@@ -29,7 +29,7 @@
"solution_imports": "from leetcode_py import ListNode",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_merge_k_lists, run_merge_k_lists\nfrom .solution import Solution",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_merge_k_lists, run_merge_k_lists\nfrom .solution import Solution",
"test_content": "",
"test_class_name": "MergeKSortedLists",
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/merge_two_sorted_lists.json b/leetcode_py/cli/resources/leetcode/json/problems/merge_two_sorted_lists.json
index a6f7231..45a89ce 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/merge_two_sorted_lists.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/merge_two_sorted_lists.json
@@ -29,7 +29,7 @@
"solution_imports": "from leetcode_py import ListNode",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_merge_two_lists, run_merge_two_lists\nfrom .solution import Solution",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_merge_two_lists, run_merge_two_lists\nfrom .solution import Solution",
"test_content": "",
"test_class_name": "MergeTwoSortedLists",
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/middle_of_the_linked_list.json b/leetcode_py/cli/resources/leetcode/json/problems/middle_of_the_linked_list.json
index f64df0a..4e333f7 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/middle_of_the_linked_list.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/middle_of_the_linked_list.json
@@ -30,7 +30,7 @@
"solution_imports": "from leetcode_py import ListNode",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_middle_node, run_middle_node\nfrom .solution import Solution",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_middle_node, run_middle_node\nfrom .solution import Solution",
"test_content": "",
"test_class_name": "MiddleOfTheLinkedList",
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/min_stack.json b/leetcode_py/cli/resources/leetcode/json/problems/min_stack.json
index 7ed6490..739f13e 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/min_stack.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/min_stack.json
@@ -27,7 +27,7 @@
"solution_imports": "",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_min_stack_operations, run_min_stack_operations\nfrom .solution import MinStack",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_min_stack_operations, run_min_stack_operations\nfrom .solution import MinStack",
"test_content": "",
"test_class_name": "TestMinStack",
"test_class_content": "",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/minimum_height_trees.json b/leetcode_py/cli/resources/leetcode/json/problems/minimum_height_trees.json
index 3378d91..c4d3133 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/minimum_height_trees.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/minimum_height_trees.json
@@ -30,7 +30,7 @@
"solution_imports": "",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_find_min_height_trees, run_find_min_height_trees\nfrom .solution import Solution",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_find_min_height_trees, run_find_min_height_trees\nfrom .solution import Solution",
"test_content": "",
"test_class_name": "MinimumHeightTrees",
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/minimum_window_substring.json b/leetcode_py/cli/resources/leetcode/json/problems/minimum_window_substring.json
index 7032f45..efaef29 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/minimum_window_substring.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/minimum_window_substring.json
@@ -33,7 +33,7 @@
"solution_imports": "",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_min_window, run_min_window\nfrom .solution import Solution",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_min_window, run_min_window\nfrom .solution import Solution",
"test_content": "",
"test_class_name": "MinimumWindowSubstring",
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/number_of_islands.json b/leetcode_py/cli/resources/leetcode/json/problems/number_of_islands.json
index 66f120d..edf3ea2 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/number_of_islands.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/number_of_islands.json
@@ -30,7 +30,7 @@
"solution_imports": "",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_num_islands, run_num_islands\nfrom .solution import Solution",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_num_islands, run_num_islands\nfrom .solution import Solution",
"test_content": "",
"test_class_name": "NumberOfIslands",
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/partition_equal_subset_sum.json b/leetcode_py/cli/resources/leetcode/json/problems/partition_equal_subset_sum.json
index 64fa8db..dd0becc 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/partition_equal_subset_sum.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/partition_equal_subset_sum.json
@@ -30,7 +30,7 @@
"solution_imports": "",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_can_partition, run_can_partition\nfrom .solution import Solution",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_can_partition, run_can_partition\nfrom .solution import Solution",
"test_content": "",
"test_class_name": "PartitionEqualSubsetSum",
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/permutations.json b/leetcode_py/cli/resources/leetcode/json/problems/permutations.json
index cb974c4..10960fe 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/permutations.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/permutations.json
@@ -29,7 +29,7 @@
"solution_imports": "",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_permute, run_permute\nfrom .solution import Solution",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_permute, run_permute\nfrom .solution import Solution",
"test_content": "",
"test_class_name": "Permutations",
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/product_of_array_except_self.json b/leetcode_py/cli/resources/leetcode/json/problems/product_of_array_except_self.json
index d698e0c..7497565 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/product_of_array_except_self.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/product_of_array_except_self.json
@@ -26,7 +26,7 @@
"solution_imports": "",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_product_except_self, run_product_except_self\nfrom .solution import Solution",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_product_except_self, run_product_except_self\nfrom .solution import Solution",
"test_content": "",
"test_class_name": "ProductOfArrayExceptSelf",
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/ransom_note.json b/leetcode_py/cli/resources/leetcode/json/problems/ransom_note.json
index 6354724..9425e00 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/ransom_note.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/ransom_note.json
@@ -27,7 +27,7 @@
"solution_imports": "",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_can_construct, run_can_construct\nfrom .solution import Solution",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_can_construct, run_can_construct\nfrom .solution import Solution",
"test_content": "",
"test_class_name": "RansomNote",
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/reverse_linked_list.json b/leetcode_py/cli/resources/leetcode/json/problems/reverse_linked_list.json
index 9ea3b51..5c01cfb 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/reverse_linked_list.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/reverse_linked_list.json
@@ -31,7 +31,7 @@
"solution_imports": "from leetcode_py import ListNode",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_reverse_list, run_reverse_list\nfrom .solution import Solution",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_reverse_list, run_reverse_list\nfrom .solution import Solution",
"test_content": "",
"test_class_name": "ReverseLinkedList",
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/reverse_linked_list_ii.json b/leetcode_py/cli/resources/leetcode/json/problems/reverse_linked_list_ii.json
index 75d9318..0fd9061 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/reverse_linked_list_ii.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/reverse_linked_list_ii.json
@@ -28,7 +28,7 @@
"solution_imports": "from leetcode_py import ListNode",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_reverse_between, run_reverse_between\nfrom .solution import Solution",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_reverse_between, run_reverse_between\nfrom .solution import Solution",
"test_content": "",
"test_class_name": "ReverseLinkedListII",
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/rotting_oranges.json b/leetcode_py/cli/resources/leetcode/json/problems/rotting_oranges.json
index 6081ed4..f4eb7a5 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/rotting_oranges.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/rotting_oranges.json
@@ -33,7 +33,7 @@
"solution_imports": "",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_oranges_rotting, run_oranges_rotting\nfrom .solution import Solution",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_oranges_rotting, run_oranges_rotting\nfrom .solution import Solution",
"test_content": "",
"test_class_name": "RottingOranges",
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/search_in_rotated_sorted_array.json b/leetcode_py/cli/resources/leetcode/json/problems/search_in_rotated_sorted_array.json
index b20a54c..aae6977 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/search_in_rotated_sorted_array.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/search_in_rotated_sorted_array.json
@@ -27,7 +27,7 @@
"solution_imports": "",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_search, run_search\nfrom .solution import Solution",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_search, run_search\nfrom .solution import Solution",
"test_content": "",
"test_class_name": "SearchInRotatedSortedArray",
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/serialize_and_deserialize_binary_tree.json b/leetcode_py/cli/resources/leetcode/json/problems/serialize_and_deserialize_binary_tree.json
index e45c6e4..4b4b99c 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/serialize_and_deserialize_binary_tree.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/serialize_and_deserialize_binary_tree.json
@@ -28,7 +28,7 @@
"solution_imports": "from leetcode_py import TreeNode",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_serialize_deserialize, run_serialize_deserialize\nfrom .solution import Codec",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_serialize_deserialize, run_serialize_deserialize\nfrom .solution import Codec",
"test_content": "",
"test_class_name": "SerializeAndDeserializeBinaryTree",
"test_class_content": "",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/sort_colors.json b/leetcode_py/cli/resources/leetcode/json/problems/sort_colors.json
index 6633cb0..1628617 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/sort_colors.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/sort_colors.json
@@ -26,7 +26,7 @@
"solution_imports": "",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_sort_colors, run_sort_colors\nfrom .solution import Solution",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_sort_colors, run_sort_colors\nfrom .solution import Solution",
"test_content": "",
"test_class_name": "SortColors",
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/spiral_matrix.json b/leetcode_py/cli/resources/leetcode/json/problems/spiral_matrix.json
index 355427b..561dd75 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/spiral_matrix.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/spiral_matrix.json
@@ -30,7 +30,7 @@
"solution_imports": "",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_spiral_order, run_spiral_order\nfrom .solution import Solution",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_spiral_order, run_spiral_order\nfrom .solution import Solution",
"test_content": "",
"test_class_name": "SpiralMatrix",
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/string_to_integer_atoi.json b/leetcode_py/cli/resources/leetcode/json/problems/string_to_integer_atoi.json
index 781b2c8..2cb7afa 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/string_to_integer_atoi.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/string_to_integer_atoi.json
@@ -39,7 +39,7 @@
"solution_imports": "",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_my_atoi, run_my_atoi\nfrom .solution import Solution",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_my_atoi, run_my_atoi\nfrom .solution import Solution",
"test_content": "",
"test_class_name": "StringToIntegerAtoi",
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/subsets.json b/leetcode_py/cli/resources/leetcode/json/problems/subsets.json
index a30f10a..fe72687 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/subsets.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/subsets.json
@@ -28,7 +28,7 @@
"solution_imports": "",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_subsets, run_subsets\nfrom .solution import Solution",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_subsets, run_subsets\nfrom .solution import Solution",
"test_content": "",
"test_class_name": "Subsets",
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/task_scheduler.json b/leetcode_py/cli/resources/leetcode/json/problems/task_scheduler.json
index a416607..963ebfb 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/task_scheduler.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/task_scheduler.json
@@ -33,7 +33,7 @@
"solution_imports": "",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_least_interval, run_least_interval\nfrom .solution import Solution",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_least_interval, run_least_interval\nfrom .solution import Solution",
"test_content": "",
"test_class_name": "TaskScheduler",
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/three_sum.json b/leetcode_py/cli/resources/leetcode/json/problems/three_sum.json
index 2f099e1..ed261d7 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/three_sum.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/three_sum.json
@@ -33,7 +33,7 @@
"solution_imports": "",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_three_sum, run_three_sum\nfrom .solution import Solution",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_three_sum, run_three_sum\nfrom .solution import Solution",
"test_content": "",
"test_class_name": "ThreeSum",
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/time_based_key_value_store.json b/leetcode_py/cli/resources/leetcode/json/problems/time_based_key_value_store.json
index 529ce24..1b4be43 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/time_based_key_value_store.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/time_based_key_value_store.json
@@ -27,7 +27,7 @@
"solution_imports": "",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_time_map_operations, run_time_map_operations\nfrom .solution import TimeMap",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_time_map_operations, run_time_map_operations\nfrom .solution import TimeMap",
"test_content": "",
"test_class_name": "TimeBasedKeyValueStore",
"test_class_content": "",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/trapping_rain_water.json b/leetcode_py/cli/resources/leetcode/json/problems/trapping_rain_water.json
index 4a21e57..d72b4ae 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/trapping_rain_water.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/trapping_rain_water.json
@@ -28,7 +28,7 @@
"solution_imports": "",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_trap, run_trap\nfrom .solution import Solution",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_trap, run_trap\nfrom .solution import Solution",
"test_content": "",
"test_class_name": "TrappingRainWater",
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/two_sum.json b/leetcode_py/cli/resources/leetcode/json/problems/two_sum.json
index a1c69c3..c79d490 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/two_sum.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/two_sum.json
@@ -29,7 +29,7 @@
"solution_imports": "",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_two_sum, run_two_sum\nfrom .solution import Solution",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_two_sum, run_two_sum\nfrom .solution import Solution",
"test_content": "",
"test_class_name": "TwoSum",
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/unique_paths.json b/leetcode_py/cli/resources/leetcode/json/problems/unique_paths.json
index 001900d..afca70e 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/unique_paths.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/unique_paths.json
@@ -30,7 +30,7 @@
"solution_imports": "",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_unique_paths, run_unique_paths\nfrom .solution import Solution",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_unique_paths, run_unique_paths\nfrom .solution import Solution",
"test_content": "",
"test_class_name": "UniquePaths",
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/valid_anagram.json b/leetcode_py/cli/resources/leetcode/json/problems/valid_anagram.json
index 2a0c3ce..36e12bc 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/valid_anagram.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/valid_anagram.json
@@ -26,7 +26,7 @@
"solution_imports": "",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_is_anagram, run_is_anagram\nfrom .solution import Solution",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_is_anagram, run_is_anagram\nfrom .solution import Solution",
"test_content": "",
"test_class_name": "ValidAnagram",
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/valid_palindrome.json b/leetcode_py/cli/resources/leetcode/json/problems/valid_palindrome.json
index 4f84dc9..69ad19e 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/valid_palindrome.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/valid_palindrome.json
@@ -33,7 +33,7 @@
"solution_imports": "",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_is_palindrome, run_is_palindrome\nfrom .solution import Solution",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_is_palindrome, run_is_palindrome\nfrom .solution import Solution",
"test_content": "",
"test_class_name": "ValidPalindrome",
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/valid_parentheses.json b/leetcode_py/cli/resources/leetcode/json/problems/valid_parentheses.json
index 554ca10..81b406c 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/valid_parentheses.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/valid_parentheses.json
@@ -29,7 +29,7 @@
"solution_imports": "",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_is_valid, run_is_valid\nfrom .solution import Solution",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_is_valid, run_is_valid\nfrom .solution import Solution",
"test_content": "",
"test_class_name": "ValidParentheses",
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/validate_binary_search_tree.json b/leetcode_py/cli/resources/leetcode/json/problems/validate_binary_search_tree.json
index b0202c1..24095c4 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/validate_binary_search_tree.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/validate_binary_search_tree.json
@@ -30,7 +30,7 @@
"solution_imports": "from leetcode_py import TreeNode",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_is_valid_bst, run_is_valid_bst\nfrom .solution import Solution",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_is_valid_bst, run_is_valid_bst\nfrom .solution import Solution",
"test_content": "",
"test_class_name": "ValidateBinarySearchTree",
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/word_break.json b/leetcode_py/cli/resources/leetcode/json/problems/word_break.json
index 28a1de4..89d9d1b 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/word_break.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/word_break.json
@@ -33,7 +33,7 @@
"solution_imports": "",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_word_break, run_word_break\nfrom .solution import Solution",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_word_break, run_word_break\nfrom .solution import Solution",
"test_content": "",
"test_class_name": "WordBreak",
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/word_ladder.json b/leetcode_py/cli/resources/leetcode/json/problems/word_ladder.json
index 216257a..102223a 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/word_ladder.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/word_ladder.json
@@ -30,7 +30,7 @@
"solution_imports": "",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_ladder_length, run_ladder_length\nfrom .solution import Solution",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_ladder_length, run_ladder_length\nfrom .solution import Solution",
"test_content": "",
"test_class_name": "WordLadder",
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/word_search.json b/leetcode_py/cli/resources/leetcode/json/problems/word_search.json
index 776a490..ea30fc4 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/word_search.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/word_search.json
@@ -33,7 +33,7 @@
"solution_imports": "",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_exist, run_exist\nfrom .solution import Solution",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_exist, run_exist\nfrom .solution import Solution",
"test_content": "",
"test_class_name": "WordSearch",
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/zero_one_matrix.json b/leetcode_py/cli/resources/leetcode/json/problems/zero_one_matrix.json
index f0bac0c..21aca93 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/zero_one_matrix.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/zero_one_matrix.json
@@ -30,7 +30,7 @@
"solution_imports": "",
"solution_contents": "",
"solution_class_content": "",
- "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_update_matrix, run_update_matrix\nfrom .solution import Solution",
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_update_matrix, run_update_matrix\nfrom .solution import Solution",
"test_content": "",
"test_class_name": "ZeroOneMatrix",
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
diff --git a/leetcode_py/cli/resources/leetcode/json/tags.json5 b/leetcode_py/cli/resources/leetcode/json/tags.json5
index ac0d614..f09def5 100644
--- a/leetcode_py/cli/resources/leetcode/json/tags.json5
+++ b/leetcode_py/cli/resources/leetcode/json/tags.json5
@@ -75,13 +75,11 @@
"word_break",
"word_ladder",
"word_search",
- "zero_one_matrix"
+ "zero_one_matrix",
],
+ grind: [{ tag: "grind-75" }, "daily_temperatures"],
+
// Test tag for development and testing
- "test": [
- "two_sum",
- "valid_palindrome",
- "binary_search"
- ]
+ test: ["binary_search", "two_sum", "valid_palindrome"],
}
diff --git a/leetcode_py/cli/utils/problem_finder.py b/leetcode_py/cli/utils/problem_finder.py
index 5943b5c..9a12ba2 100644
--- a/leetcode_py/cli/utils/problem_finder.py
+++ b/leetcode_py/cli/utils/problem_finder.py
@@ -51,22 +51,43 @@ def get_all_problems() -> list[str]:
return [json_file.stem for json_file in json_path.glob("*.json")]
+def _add_problem_to_tag_map(
+ problem_tags_map: dict[str, list[str]], problem_name: str, tag_name: str
+) -> None:
+ if problem_name not in problem_tags_map:
+ problem_tags_map[problem_name] = []
+ problem_tags_map[problem_name].append(tag_name)
+
+
+def _process_tag_reference(
+ tags_data: dict, item: dict, tag_name: str, problem_tags_map: dict[str, list[str]]
+) -> None:
+ for problem_name in tags_data.get(item["tag"], []):
+ if isinstance(problem_name, str):
+ _add_problem_to_tag_map(problem_tags_map, problem_name, tag_name)
+
+
+def _process_tag_item(
+ tags_data: dict, item: str | dict, tag_name: str, problem_tags_map: dict[str, list[str]]
+) -> None:
+ if isinstance(item, dict) and "tag" in item:
+ _process_tag_reference(tags_data, item, tag_name, problem_tags_map)
+ elif isinstance(item, str):
+ _add_problem_to_tag_map(problem_tags_map, item, tag_name)
+
+
@lru_cache(maxsize=1)
def _build_problem_tags_cache() -> dict[str, list[str]]:
- tags_file = get_tags_path()
- problem_tags_map: dict[str, list[str]] = {}
-
try:
- with open(tags_file) as f:
+ with open(get_tags_path()) as f:
tags_data = json5.load(f)
- # Build reverse mapping: problem -> list of tags
+ problem_tags_map: dict[str, list[str]] = {}
+
for tag_name, problems in tags_data.items():
if isinstance(problems, list):
- for problem_name in problems:
- if problem_name not in problem_tags_map:
- problem_tags_map[problem_name] = []
- problem_tags_map[problem_name].append(tag_name)
+ for item in problems:
+ _process_tag_item(tags_data, item, tag_name, problem_tags_map)
return problem_tags_map
except (ValueError, OSError, KeyError):
diff --git a/leetcode_py/tools/generator.py b/leetcode_py/tools/generator.py
index 4474cb9..257491c 100644
--- a/leetcode_py/tools/generator.py
+++ b/leetcode_py/tools/generator.py
@@ -5,6 +5,8 @@
import typer
from cookiecutter.main import cookiecutter
+from leetcode_py.cli.utils.problem_finder import get_tags_for_problem
+
def load_json_data(json_path: Path) -> dict:
if not json_path.exists():
@@ -44,9 +46,34 @@ def format_python_files(problem_dir: Path) -> None:
pass
+def merge_tags(data: dict) -> dict:
+ """Merge tags from get_tags_for_problem with existing JSON tags."""
+ problem_name = data.get("problem_name", "")
+ if not problem_name:
+ return data
+
+ # Get tags from tag system
+ system_tags = get_tags_for_problem(problem_name)
+
+ # Get existing tags from JSON
+ existing_tags = data.get("_tags", {}).get("list", [])
+
+ # Merge and deduplicate tags
+ all_tags = list(set(system_tags + existing_tags))
+
+ # Update data with merged tags
+ if all_tags:
+ data["_tags"] = {"list": all_tags}
+
+ return data
+
+
def generate_from_template(data: dict, template_dir: Path, output_dir: Path) -> None:
"""Generate problem files using cookiecutter template."""
try:
+ # Merge tags before generating
+ data = merge_tags(data)
+
cookiecutter(
str(template_dir),
extra_context=data,
diff --git a/leetcode_py/test_utils.py b/leetcode_py/tools/logged_test.py
similarity index 100%
rename from leetcode_py/test_utils.py
rename to leetcode_py/tools/logged_test.py
diff --git a/poetry.lock b/poetry.lock
index c88ef01..b6650a4 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -95,6 +95,7 @@ files = [
[package.dependencies]
pycodestyle = ">=2.12.0"
+tomli = {version = "*", markers = "python_version < \"3.11\""}
[[package]]
name = "binaryornot"
@@ -149,6 +150,8 @@ mypy-extensions = ">=0.4.3"
packaging = ">=22.0"
pathspec = ">=0.9.0"
platformdirs = ">=2"
+tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
+typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""}
[package.extras]
colorama = ["colorama (>=0.4.3)"]
@@ -542,6 +545,9 @@ files = [
{file = "coverage-7.10.6.tar.gz", hash = "sha256:f644a3ae5933a552a29dbb9aa2f90c677a875f80ebea028e5a52a4f429044b90"},
]
+[package.dependencies]
+tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""}
+
[package.extras]
toml = ["tomli ; python_full_version <= \"3.11.0a6\""]
@@ -605,6 +611,25 @@ files = [
{file = "distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d"},
]
+[[package]]
+name = "exceptiongroup"
+version = "1.3.0"
+description = "Backport of PEP 654 (exception groups)"
+optional = false
+python-versions = ">=3.7"
+groups = ["dev"]
+markers = "python_version == \"3.10\""
+files = [
+ {file = "exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10"},
+ {file = "exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88"},
+]
+
+[package.dependencies]
+typing-extensions = {version = ">=4.6.0", markers = "python_version < \"3.13\""}
+
+[package.extras]
+test = ["pytest (>=6)"]
+
[[package]]
name = "executing"
version = "2.2.1"
@@ -742,20 +767,20 @@ test = ["flaky", "ipyparallel", "pre-commit", "pytest (>=7.0,<9)", "pytest-async
[[package]]
name = "ipython"
-version = "9.5.0"
+version = "8.37.0"
description = "IPython: Productive Interactive Computing"
optional = false
-python-versions = ">=3.11"
+python-versions = ">=3.10"
groups = ["dev"]
files = [
- {file = "ipython-9.5.0-py3-none-any.whl", hash = "sha256:88369ffa1d5817d609120daa523a6da06d02518e582347c29f8451732a9c5e72"},
- {file = "ipython-9.5.0.tar.gz", hash = "sha256:129c44b941fe6d9b82d36fc7a7c18127ddb1d6f02f78f867f402e2e3adde3113"},
+ {file = "ipython-8.37.0-py3-none-any.whl", hash = "sha256:ed87326596b878932dbcb171e3e698845434d8c61b8d8cd474bf663041a9dcf2"},
+ {file = "ipython-8.37.0.tar.gz", hash = "sha256:ca815841e1a41a1e6b73a0b08f3038af9b2252564d01fc405356d34033012216"},
]
[package.dependencies]
colorama = {version = "*", markers = "sys_platform == \"win32\""}
decorator = "*"
-ipython-pygments-lexers = "*"
+exceptiongroup = {version = "*", markers = "python_version < \"3.11\""}
jedi = ">=0.16"
matplotlib-inline = "*"
pexpect = {version = ">4.3", markers = "sys_platform != \"win32\" and sys_platform != \"emscripten\""}
@@ -763,29 +788,21 @@ prompt_toolkit = ">=3.0.41,<3.1.0"
pygments = ">=2.4.0"
stack_data = "*"
traitlets = ">=5.13.0"
+typing_extensions = {version = ">=4.6", markers = "python_version < \"3.12\""}
[package.extras]
-all = ["ipython[doc,matplotlib,test,test-extra]"]
+all = ["ipython[black,doc,kernel,matplotlib,nbconvert,nbformat,notebook,parallel,qtconsole]", "ipython[test,test-extra]"]
black = ["black"]
-doc = ["docrepr", "exceptiongroup", "intersphinx_registry", "ipykernel", "ipython[test]", "matplotlib", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "sphinx_toml (==0.0.4)", "typing_extensions"]
+doc = ["docrepr", "exceptiongroup", "intersphinx_registry", "ipykernel", "ipython[test]", "matplotlib", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "sphinxcontrib-jquery", "tomli ; python_version < \"3.11\"", "typing_extensions"]
+kernel = ["ipykernel"]
matplotlib = ["matplotlib"]
-test = ["packaging", "pytest", "pytest-asyncio", "testpath"]
-test-extra = ["curio", "ipykernel", "ipython[test]", "jupyter_ai", "matplotlib (!=3.2.0)", "nbclient", "nbformat", "numpy (>=1.23)", "pandas", "trio"]
-
-[[package]]
-name = "ipython-pygments-lexers"
-version = "1.1.1"
-description = "Defines a variety of Pygments lexers for highlighting IPython code."
-optional = false
-python-versions = ">=3.8"
-groups = ["dev"]
-files = [
- {file = "ipython_pygments_lexers-1.1.1-py3-none-any.whl", hash = "sha256:a9462224a505ade19a605f71f8fa63c2048833ce50abc86768a0d81d876dc81c"},
- {file = "ipython_pygments_lexers-1.1.1.tar.gz", hash = "sha256:09c0138009e56b6854f9535736f4171d855c8c08a563a0dcd8022f78355c7e81"},
-]
-
-[package.dependencies]
-pygments = "*"
+nbconvert = ["nbconvert"]
+nbformat = ["nbformat"]
+notebook = ["ipywidgets", "notebook"]
+parallel = ["ipyparallel"]
+qtconsole = ["qtconsole"]
+test = ["packaging", "pickleshare", "pytest", "pytest-asyncio (<0.22)", "testpath"]
+test-extra = ["curio", "ipython[test]", "jupyter_ai", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.23)", "pandas", "trio"]
[[package]]
name = "isort"
@@ -955,6 +972,7 @@ mdit-py-plugins = "*"
nbformat = "*"
packaging = "*"
pyyaml = "*"
+tomli = {version = "*", markers = "python_version < \"3.11\""}
[package.extras]
dev = ["autopep8", "black", "flake8", "gitpython", "ipykernel", "isort", "jupyter-fs[fs] (>=1.0)", "jupyter-server (!=2.11)", "nbconvert", "pre-commit", "pytest", "pytest-asyncio", "pytest-cov (>=2.6.1)", "pytest-randomly", "pytest-xdist", "sphinx", "sphinx-gallery (>=0.8)"]
@@ -1178,6 +1196,7 @@ files = [
[package.dependencies]
mypy_extensions = ">=1.0.0"
pathspec = ">=0.9.0"
+tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
typing_extensions = ">=4.6.0"
[package.extras]
@@ -1495,10 +1514,12 @@ files = [
[package.dependencies]
colorama = {version = ">=0.4", markers = "sys_platform == \"win32\""}
+exceptiongroup = {version = ">=1", markers = "python_version < \"3.11\""}
iniconfig = ">=1"
packaging = ">=20"
pluggy = ">=1.5,<2"
pygments = ">=2.7.2"
+tomli = {version = ">=1", markers = "python_version < \"3.11\""}
[package.extras]
dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "requests", "setuptools", "xmlschema"]
@@ -1770,6 +1791,7 @@ files = [
[package.dependencies]
attrs = ">=22.2.0"
rpds-py = ">=0.7.0"
+typing-extensions = {version = ">=4.4.0", markers = "python_version < \"3.13\""}
[[package]]
name = "requests"
@@ -2080,7 +2102,7 @@ version = "2.2.1"
description = "A lil' TOML parser"
optional = false
python-versions = ">=3.8"
-groups = ["dev"]
+groups = ["main", "dev"]
files = [
{file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"},
{file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"},
@@ -2115,6 +2137,7 @@ files = [
{file = "tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc"},
{file = "tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"},
]
+markers = {main = "python_version == \"3.10\""}
[[package]]
name = "tornado"
@@ -2230,6 +2253,7 @@ files = [
distlib = ">=0.3.7,<1"
filelock = ">=3.12.2,<4"
platformdirs = ">=3.9.1,<5"
+typing-extensions = {version = ">=4.13.2", markers = "python_version < \"3.11\""}
[package.extras]
docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"]
@@ -2265,5 +2289,5 @@ dev = ["black (>=19.3b0) ; python_version >= \"3.6\"", "pytest (>=4.6.2)"]
[metadata]
lock-version = "2.1"
-python-versions = "^3.13"
-content-hash = "11af98f38dd5768ebe54804d96677bc209cd0927a64f14c2110dde92d9496301"
+python-versions = ">=3.10,<4.0"
+content-hash = "36c5064361f5582ade3b260efc37e6c576cf1fd3d97f2608b1b7d333d19caee7"
diff --git a/pyproject.toml b/pyproject.toml
index 28663d1..1a07d33 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -16,6 +16,9 @@ classifiers = [
"Topic :: Education",
"License :: OSI Approved :: Apache Software License",
"Programming Language :: Python :: 3",
+ "Programming Language :: Python :: 3.10",
+ "Programming Language :: Python :: 3.11",
+ "Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
]
packages = [{include = "leetcode_py"}]
@@ -25,7 +28,7 @@ include = ["leetcode_py/cli/resources/**/*"]
lcpy = "leetcode_py.cli.main:app"
[tool.poetry.dependencies]
-python = "^3.13"
+python = ">=3.10,<4.0"
anytree = "^2.13.0"
black = "^25.1.0"
cookiecutter = "^2.6.0"
@@ -58,7 +61,7 @@ poetry-plugin-sort = { version = ">=0.2.2", extras = ["plugin"] }
[tool.black]
line-length = 105
-target-version = ['py312']
+target-version = ['py310']
include = '.*\.(py|ipynb)$' # All .py and .ipynb files
extend-exclude = '''
/(
@@ -78,7 +81,7 @@ extend_skip_glob = ["leetcode_py/cli/resources/*"]
[tool.ruff]
line-length = 105
-target-version = 'py312'
+target-version = 'py310'
extend-exclude = ["leetcode_py/cli/resources"]
[tool.ruff.lint.pydocstyle]
diff --git a/scripts/sort_tags.py b/scripts/sort_tags.py
new file mode 100644
index 0000000..94aa364
--- /dev/null
+++ b/scripts/sort_tags.py
@@ -0,0 +1,30 @@
+#!/usr/bin/env python3
+import sys
+
+import json5
+
+tags_file = "leetcode_py/cli/resources/leetcode/json/tags.json5"
+
+with open(tags_file) as f:
+ data = json5.load(f)
+
+unsorted_tags = []
+for tag_name, problems in data.items():
+ if isinstance(problems, list):
+ dicts = [item for item in problems if isinstance(item, dict)]
+ strings = [item for item in problems if isinstance(item, str)]
+ expected_order = dicts + sorted(strings)
+
+ if problems != expected_order:
+ unsorted_tags.append((tag_name, problems, expected_order))
+
+if unsorted_tags:
+ print("❌ Found unsorted problem lists:")
+ for tag_name, current, expected in unsorted_tags:
+ print(f"\n{tag_name}:")
+ print(f" Current: {current}")
+ print(f" Expected: {expected}")
+ sys.exit(1)
+else:
+ print("✅ All problem lists are sorted!")
+ sys.exit(0)
diff --git a/tests/test_problem_finder.py b/tests/test_problem_finder.py
new file mode 100644
index 0000000..1d0aac6
--- /dev/null
+++ b/tests/test_problem_finder.py
@@ -0,0 +1,25 @@
+from leetcode_py.cli.utils.problem_finder import _build_problem_tags_cache, get_tags_for_problem
+
+
+def test_build_problem_tags_cache_with_real_tags():
+ result = _build_problem_tags_cache()
+
+ # Test that grind tag includes both grind-75 problems and daily_temperatures
+ assert "daily_temperatures" in result
+ assert "grind" in result["daily_temperatures"]
+
+ # Test that grind-75 problems also get grind tag
+ assert "two_sum" in result
+ assert "grind-75" in result["two_sum"]
+ assert "grind" in result["two_sum"]
+
+
+def test_get_tags_for_problem():
+ # Test daily_temperatures has grind tag
+ tags = get_tags_for_problem("daily_temperatures")
+ assert "grind" in tags
+
+ # Test grind-75 problem has both tags
+ tags = get_tags_for_problem("two_sum")
+ assert "grind-75" in tags
+ assert "grind" in tags
diff --git a/tests/test_test_utils.py b/tests/test_test_utils.py
index 31533cd..3e4eb3c 100644
--- a/tests/test_test_utils.py
+++ b/tests/test_test_utils.py
@@ -2,7 +2,7 @@
import pytest
-from leetcode_py.test_utils import logged_test
+from leetcode_py import logged_test
class TestLoggedTest: