From 04a198ecff16f6169d7167612fa8717e40afe94b Mon Sep 17 00:00:00 2001 From: Eduardo Pujol Date: Mon, 27 Jan 2025 18:44:24 -0500 Subject: [PATCH 1/4] Docs cleanup --- flask_to_fastapi_migration/guide.md | 75 -------------- freezegun_to_timemachine_migration/run.py | 99 +++++++++++-------- python2_to_python3/run.py | 9 +- unittest_to_pytest/README.md | 27 +++-- unittest_to_pytest/guide.md | 41 -------- .../repo-after/jj_classes/__init__.py | 0 .../repo-after/jj_classes/castle.py | 28 ------ .../repo-after/jj_classes/character.py | 23 ----- .../repo-after/tests/__init__.py | 1 - .../repo-after/tests/test_classes.py | 93 ----------------- .../repo-before/jj_classes/__init__.py | 0 .../repo-before/jj_classes/castle.py | 28 ------ .../repo-before/jj_classes/character.py | 23 ----- unittest_to_pytest/repo-before/run_tests.py | 9 -- .../repo-before/tests/__init__.py | 0 .../repo-before/tests/test_classes.py | 90 ----------------- unittest_to_pytest/run.py | 7 +- 17 files changed, 86 insertions(+), 467 deletions(-) delete mode 100644 flask_to_fastapi_migration/guide.md delete mode 100644 unittest_to_pytest/guide.md delete mode 100644 unittest_to_pytest/repo-after/jj_classes/__init__.py delete mode 100644 unittest_to_pytest/repo-after/jj_classes/castle.py delete mode 100644 unittest_to_pytest/repo-after/jj_classes/character.py delete mode 100644 unittest_to_pytest/repo-after/tests/__init__.py delete mode 100644 unittest_to_pytest/repo-after/tests/test_classes.py delete mode 100644 unittest_to_pytest/repo-before/jj_classes/__init__.py delete mode 100644 unittest_to_pytest/repo-before/jj_classes/castle.py delete mode 100644 unittest_to_pytest/repo-before/jj_classes/character.py delete mode 100644 unittest_to_pytest/repo-before/run_tests.py delete mode 100644 unittest_to_pytest/repo-before/tests/__init__.py delete mode 100644 unittest_to_pytest/repo-before/tests/test_classes.py diff --git a/flask_to_fastapi_migration/guide.md b/flask_to_fastapi_migration/guide.md deleted file mode 100644 index 929352d..0000000 --- a/flask_to_fastapi_migration/guide.md +++ /dev/null @@ -1,75 +0,0 @@ -# Guide: Migrating from Flask to FastAPI with Codegen - -This guide walks you through the steps to migrate your codebase from Flask to FastAPI using Codegen. Follow along to modernize your static file handling, template syntax, and routes while ensuring compatibility with FastAPI. Each step includes a direct link to the appropriate codemod for easy implementation. - ---- - -## 🎉 Overview of Changes - -The migration focuses on these key updates: - -1. **Static File Handling** - Migrate static file handling to FastAPI's approach. - [Run the Static File Handling Codemod](https://www.codegen.sh/search/6752?skillType=codemod) - -2. **Template Syntax Refactoring** - Refactor Jinja2 template syntax for better compatibility and readability. - [Run the Template Syntax Codemod](https://www.codegen.sh/search/6750?skillType=codemod) - -3. **Migration Feedback Enhancement** - Enhance feedback during the migration process for better tracking and debugging. - [Run the Migration Feedback Codemod](https://www.codegen.sh/search/6698?skillType=codemod) - -4. **Route Migration** - Migrate Flask routes to FastAPI's routing system. - [Run the Route Migration Codemod](https://www.codegen.sh/search/6699?skillType=codemod) - ---- - -## How to Migrate - -### Step 1: Migrate Static File Handling - -FastAPI uses a different approach for serving static files. Use the static file handling codemod to: - -- Update your static file serving logic to align with FastAPI's methods. - -👉 [Run the Static File Handling Codemod](https://www.codegen.sh/search/6752?skillType=codemod) - ---- - -### Step 2: Refactor Template Syntax - -Refactor your Jinja2 template syntax to ensure compatibility with FastAPI. This includes: - -- Updating template rendering logic. -- Ensuring all template syntax is compatible with FastAPI. - -👉 [Run the Template Syntax Codemod](https://www.codegen.sh/search/6750?skillType=codemod) - ---- - -### Step 3: Enhance Migration Feedback - -Improve the feedback you receive during the migration process. This helps in tracking progress and debugging issues. - -👉 [Run the Migration Feedback Codemod](https://www.codegen.sh/search/6698?skillType=codemod) - ---- - -### Step 4: Migrate Flask Routes - -FastAPI has a different routing system compared to Flask. Use the route migration codemod to: - -- Convert Flask routes to FastAPI routes. -- Ensure all route handlers are updated to FastAPI's syntax. - -👉 [Run the Route Migration Codemod](https://www.codegen.sh/search/6699?skillType=codemod) - ---- - -## Need Help? - -If you encounter issues or have specific edge cases not addressed by the codemods, reach out to the Codegen support team or visit the [Codegen Documentation](https://www.codegen.sh/docs) for detailed guidance. - -Start your FastAPI migration today and enjoy the benefits of a cleaner, modern codebase! diff --git a/freezegun_to_timemachine_migration/run.py b/freezegun_to_timemachine_migration/run.py index da86ba3..cf8603f 100644 --- a/freezegun_to_timemachine_migration/run.py +++ b/freezegun_to_timemachine_migration/run.py @@ -1,42 +1,61 @@ +import codegen from codegen import Codebase -codebase = Codebase.from_repo("getmoto/moto", commit="786a8ada7ed0c7f9d8b04d49f24596865e4b7901") - -print("🚀 Starting FreezeGun to TimeMachine conversion...") - -for file in codebase.files: - if "tests" not in file.filepath: - continue - print(f"📝 Processing: {file.filepath}") - # Update imports - for imp in file.imports: - if imp.symbol_name and 'freezegun' in imp.source: - if imp.name == 'freeze_time': - # required due to Codegen limitations - imp.edit('from time_machine import travel') - else: - imp.set_import_module('time_machine') - # Find all function calls in the file - for fcall in file.function_calls: - # Skip if not a freeze_time call - if 'freeze_time' not in fcall.source: + +@codegen.function("freezegun-to-timemachine") +def run(codebase: Codebase): + """Convert FreezeGun usage to TimeMachine in test files. + + This script: + 1. Identifies test files using FreezeGun. + 2. Updates imports from FreezeGun to TimeMachine. + 3. Modifies function calls to include necessary parameters. + """ + print("🚀 Starting FreezeGun to TimeMachine conversion...") + + for file in codebase.files: + if "tests" not in file.filepath: continue - # Get original source and prepare new source - new_source = fcall.source - # Add tick parameter if not present - if not fcall.get_arg_by_parameter_name('tick'): - if new_source.endswith(')'): - new_source = new_source[:-1] - if not new_source.endswith('('): - new_source += ',' - new_source += ' tick=False)' - # Replace freeze_time with travel - if '.' in new_source: - new_source = new_source.replace( - 'freeze_time', 'travel').replace('freezegun', 'time_machine') - else: - new_source = 'travel' + new_source[len('freeze_time'):] - # Make single edit with complete changes - fcall.edit(new_source) -codebase.commit() - -print("✅ FreezeGun to TimeMachine conversion completed successfully!") + print(f"📝 Processing: {file.filepath}") + + # Update imports + for imp in file.imports: + if imp.symbol_name and 'freezegun' in imp.source: + if imp.name == 'freeze_time': + # required due to Codegen limitations + imp.edit('from time_machine import travel') + else: + imp.set_import_module('time_machine') + + # Find all function calls in the file + for fcall in file.function_calls: + # Skip if not a freeze_time call + if 'freeze_time' not in fcall.source: + continue + + # Get original source and prepare new source + new_source = fcall.source + + # Add tick parameter if not present + if not fcall.get_arg_by_parameter_name('tick'): + if new_source.endswith(')'): + new_source = new_source[:-1] + if not new_source.endswith('('): + new_source += ',' + new_source += ' tick=False)' + + # Replace freeze_time with travel + if '.' in new_source: + new_source = new_source.replace( + 'freeze_time', 'travel').replace('freezegun', 'time_machine') + else: + new_source = 'travel' + new_source[len('freeze_time'):] + + # Make single edit with complete changes + fcall.edit(new_source) + + codebase.commit() + print("✅ FreezeGun to TimeMachine conversion completed successfully!") + +if __name__ == "__main__": + codebase = Codebase.from_repo("getmoto/moto", commit="786a8ada7ed0c7f9d8b04d49f24596865e4b7901") + run(codebase) diff --git a/python2_to_python3/run.py b/python2_to_python3/run.py index 96ba108..40eb6d7 100644 --- a/python2_to_python3/run.py +++ b/python2_to_python3/run.py @@ -1,10 +1,11 @@ +import codegen from codegen import Codebase # Initialize codebase codebase = Codebase("./") # Define the target directory -TARGET_DIR = "repo-before" +TARGET_DIR = "input_repo" def convert_print_statements(file): @@ -114,8 +115,8 @@ def update_iterators(file): new_stmt = new_stmt.rstrip() + ")" stmt.edit(new_stmt) - -def main(): +@codegen.function("python2-to-python3") +def run(): """Main function to run the Python 2 to 3 conversion""" print("🚀 Starting Python 2 to 3 conversion...\n") @@ -149,4 +150,4 @@ def main(): if __name__ == "__main__": - main() + run(codebase) diff --git a/unittest_to_pytest/README.md b/unittest_to_pytest/README.md index a06b018..245fc78 100644 --- a/unittest_to_pytest/README.md +++ b/unittest_to_pytest/README.md @@ -1,12 +1,10 @@ -# unittest to pytest Migration Example +# Unittest to Pytest Migration Example -[![Documentation](https://img.shields.io/badge/docs-docs.codegen.com-blue)](https://docs.codegen.com/tutorials/unittest-to-pytest) +This codemod demonstrates how to automatically migrate `unittest` test suites to `pytest` using Codegen. The migration script simplifies the process by handling all the tedious manual updates automatically. -This example demonstrates how to use Codegen to automatically migrate unittest test suites to pytest. For a complete walkthrough, check out our [tutorial](https://docs.codegen.com/tutorials/unittest-to-pytest). +## How the Migration Script Works -## What This Example Does - -The migration script handles four key transformations: +The script automates the entire migration process in a few key steps: 1. **Convert Test Classes and Setup Methods** ```python @@ -29,6 +27,8 @@ The migration script handles four key transformations: user = db.create_user("test") assert user.name == "test" ``` + - Converts `unittest.TestCase` classes to standalone functions + - Replaces `setUp` methods with `pytest` fixtures 2. **Update Assertions** ```python @@ -45,6 +45,8 @@ The migration script handles four key transformations: with pytest.raises(ValueError): parse_id("invalid") ``` + - Replaces `unittest` assertions with `pytest` assertions + - Uses `pytest.raises` for exception testing 3. **Convert Test Discovery** ```python @@ -55,6 +57,8 @@ The migration script handles four key transformations: # To: # Remove unittest.main() and rename files to test_*.py ``` + - Removes `unittest.main()` calls + - Ensures files are named for `pytest` discovery 4. **Modernize Fixtures** ```python @@ -68,8 +72,9 @@ The migration script handles four key transformations: def conn(): return create_db() ``` + - Converts class-level setup to session-scoped fixtures -## Running the Example +## Running the Migration ```bash # Install Codegen @@ -84,7 +89,7 @@ The script will process all Python test files in the `repo-before` directory and ## Understanding the Code - `run.py` - The migration script -- `repo-before/` - Sample unittest test suite to migrate +- `repo-before/` - Sample `unittest` test suite to migrate - `guide.md` - Additional notes and explanations ## Learn More @@ -92,4 +97,8 @@ The script will process all Python test files in the `repo-before` directory and - [Full Tutorial](https://docs.codegen.com/tutorials/unittest-to-pytest) - [pytest Documentation](https://docs.pytest.org/) - [unittest Documentation](https://docs.python.org/3/library/unittest.html) -- [Codegen Documentation](https://docs.codegen.com) \ No newline at end of file +- [Codegen Documentation](https://docs.codegen.com) + +## Contributing + +Feel free to submit issues and enhancement requests! \ No newline at end of file diff --git a/unittest_to_pytest/guide.md b/unittest_to_pytest/guide.md deleted file mode 100644 index c683482..0000000 --- a/unittest_to_pytest/guide.md +++ /dev/null @@ -1,41 +0,0 @@ -# Guide: Migrating from Unittest to Pytest with Codegen - -This guide walks you through the steps to migrate your codebase from Unittest to Pytest using Codegen. Follow along to decouple your test cases from the Unittest framework and transform your setup methods into Pytest fixtures. Each step includes a direct link to the appropriate codemod for easy implementation. - ---- - -## 🎉 Overview of Changes - -The migration focuses on these key updates: - -1. **Remove Unittest Inheritance** - This codemod iterates through files in the tests directory and removes inheritance from `unittest.TestCase` for classes that have it. This helps in decoupling test cases from the Unittest framework, potentially preparing them for migration to another testing framework like Pytest. - [Run the Remove Unittest Inheritance Codemod](https://www.codegen.sh/preview/6867) - -2. **Transform setUp Methods to Pytest Fixtures** - This codemod transforms `setUp` methods in unit tests into Pytest fixtures, adding necessary imports and creating fixture functions. It updates test methods to utilize these fixtures, removes obsolete `setUp` methods, and eliminates Unittest imports, ensuring a seamless transition from Unittest to Pytest. - [Run the Transform setUp Methods Codemod](https://www.codegen.sh/preview/6919) - ---- - -## How to Migrate - -### Step 1: Remove Unittest Inheritance - -Use the codemod to remove inheritance from `unittest.TestCase` in your test classes. This will help decouple your test cases from the Unittest framework. - -👉 [Run the Remove Unittest Inheritance Codemod](https://www.codegen.sh/preview/6867) - -### Step 2: Transform setUp Methods to Pytest Fixtures - -Use the codemod to transform `setUp` methods into Pytest fixtures. This includes adding necessary imports, creating fixture functions, updating test methods to use these fixtures, and removing obsolete `setUp` methods and Unittest imports. - -👉 [Run the Transform setUp Methods Codemod](https://www.codegen.sh/preview/6919) - ---- - -## Need Help? - -If you encounter issues or have specific edge cases not addressed by the codemods, reach out to the Codegen support team or visit the [Codegen Documentation](https://www.codegen.sh/docs) for detailed guidance. - -Start your Pytest migration today and enjoy the benefits of a cleaner, modern codebase! diff --git a/unittest_to_pytest/repo-after/jj_classes/__init__.py b/unittest_to_pytest/repo-after/jj_classes/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/unittest_to_pytest/repo-after/jj_classes/castle.py b/unittest_to_pytest/repo-after/jj_classes/castle.py deleted file mode 100644 index cb95a69..0000000 --- a/unittest_to_pytest/repo-after/jj_classes/castle.py +++ /dev/null @@ -1,28 +0,0 @@ -# jj_classes/castle.py - -class Castle: - """Defines the Castle class.""" - - def __init__(self, name): - """Initialize the castle.""" - if not name: - raise ValueError("Castle name cannot be empty.") - self._name = name - self._boss = "Bowser" - self._world = "Grass Land" - - def has_access(self, character): - """Check if a character has access to the castle.""" - return character.powerup == "Super Mushroom" - - @property - def name(self): - return self._name - - @property - def boss(self): - return self._boss - - @property - def world(self): - return self._world diff --git a/unittest_to_pytest/repo-after/jj_classes/character.py b/unittest_to_pytest/repo-after/jj_classes/character.py deleted file mode 100644 index 53226be..0000000 --- a/unittest_to_pytest/repo-after/jj_classes/character.py +++ /dev/null @@ -1,23 +0,0 @@ -# jj_classes/character.py - -class Character: - """Defines the Character class.""" - - def __init__(self, name): - """Initialize the character.""" - if not name: - raise ValueError("Character name cannot be empty.") - self._name = name - self._powerup = None - - @property - def name(self): - return self._name - - @property - def powerup(self): - return self._powerup - - @powerup.setter - def powerup(self, value): - self._powerup = value diff --git a/unittest_to_pytest/repo-after/tests/__init__.py b/unittest_to_pytest/repo-after/tests/__init__.py deleted file mode 100644 index 5871ed8..0000000 --- a/unittest_to_pytest/repo-after/tests/__init__.py +++ /dev/null @@ -1 +0,0 @@ -import pytest diff --git a/unittest_to_pytest/repo-after/tests/test_classes.py b/unittest_to_pytest/repo-after/tests/test_classes.py deleted file mode 100644 index 8de04e0..0000000 --- a/unittest_to_pytest/repo-after/tests/test_classes.py +++ /dev/null @@ -1,93 +0,0 @@ -# tests/test_classes.py - -import pytest -import unittest -from unittest.mock import Mock -from jj_classes.castle import Castle -from jj_classes.character import Character - - -class TestCastle(): - """Tests for the Castle class.""" - @pytest.fixture - def setup_testcastle(): - castle = Castle("Test Castle") - - - - def test_castle_name(setup_testcastle, Castle): - """Test that the castle name is set correctly.""" - assertEqual(castle.name, "Test Castle") - - def test_castle_boss(setup_testcastle, Castle): - """Test that the default boss is Bowser.""" - assertEqual(castle.boss, "Bowser") - - def test_castle_world(setup_testcastle, Castle): - """Test that the default world is Grass Land.""" - assertEqual(castle.world, "Grass Land") - - def test_has_access_granted(setup_testcastle, Castle): - """Test that access is granted for the correct powerup.""" - character = Mock(powerup="Super Mushroom") - assertTrue(castle.has_access(character)) - - def test_has_access_denied(setup_testcastle, Castle): - """Test that access is denied for an incorrect powerup.""" - character = Mock(powerup="Starman") - assertFalse(castle.has_access(character)) - - def test_empty_name_raises_error(setup_testcastle, Castle): - """Test that an empty castle name raises a ValueError.""" - with assertRaises(ValueError): - Castle("") - - -class TestCharacter(): - """Tests for the Character class.""" - @pytest.fixture - def setup_testcharacter(): - character = Character("Mario") - - - - def test_character_name(setup_testcharacter, Character): - """Test that the character name is set correctly.""" - assertEqual(character.name, "Mario") - - def test_default_powerup(setup_testcharacter, Character): - """Test that the default powerup is None.""" - assertIsNone(character.powerup) - - def test_set_powerup(setup_testcharacter, Character): - """Test setting a powerup.""" - character.powerup = "Fire Flower" - assertEqual(character.powerup, "Fire Flower") - - def test_empty_name_raises_error(setup_testcharacter, Character): - """Test that an empty character name raises a ValueError.""" - with assertRaises(ValueError): - Character("") - - -class TestCastleAndCharacter(unittest.TestCase): - """Tests for the interaction between Castle and Character.""" - @pytest.fixture - def setup_testcastleandcharacter(): - character = Character("Mario") - - - - def test_character_has_access(setup_testcastleandcharacter, Character): - """Test that a character with the correct powerup has access.""" - character.powerup = "Super Mushroom" - assertTrue(castle.has_access(character)) - - def test_character_denied_access(setup_testcastleandcharacter, Character): - """Test that a character with the wrong powerup is denied access.""" - character.powerup = "Starman" - assertFalse(castle.has_access(character)) - - -if __name__ == "__main__": - unittest.main() diff --git a/unittest_to_pytest/repo-before/jj_classes/__init__.py b/unittest_to_pytest/repo-before/jj_classes/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/unittest_to_pytest/repo-before/jj_classes/castle.py b/unittest_to_pytest/repo-before/jj_classes/castle.py deleted file mode 100644 index cb95a69..0000000 --- a/unittest_to_pytest/repo-before/jj_classes/castle.py +++ /dev/null @@ -1,28 +0,0 @@ -# jj_classes/castle.py - -class Castle: - """Defines the Castle class.""" - - def __init__(self, name): - """Initialize the castle.""" - if not name: - raise ValueError("Castle name cannot be empty.") - self._name = name - self._boss = "Bowser" - self._world = "Grass Land" - - def has_access(self, character): - """Check if a character has access to the castle.""" - return character.powerup == "Super Mushroom" - - @property - def name(self): - return self._name - - @property - def boss(self): - return self._boss - - @property - def world(self): - return self._world diff --git a/unittest_to_pytest/repo-before/jj_classes/character.py b/unittest_to_pytest/repo-before/jj_classes/character.py deleted file mode 100644 index 53226be..0000000 --- a/unittest_to_pytest/repo-before/jj_classes/character.py +++ /dev/null @@ -1,23 +0,0 @@ -# jj_classes/character.py - -class Character: - """Defines the Character class.""" - - def __init__(self, name): - """Initialize the character.""" - if not name: - raise ValueError("Character name cannot be empty.") - self._name = name - self._powerup = None - - @property - def name(self): - return self._name - - @property - def powerup(self): - return self._powerup - - @powerup.setter - def powerup(self, value): - self._powerup = value diff --git a/unittest_to_pytest/repo-before/run_tests.py b/unittest_to_pytest/repo-before/run_tests.py deleted file mode 100644 index 7417397..0000000 --- a/unittest_to_pytest/repo-before/run_tests.py +++ /dev/null @@ -1,9 +0,0 @@ -# run_tests.py - -import unittest - -if __name__ == "__main__": - loader = unittest.TestLoader() - tests = loader.discover("tests") - test_runner = unittest.TextTestRunner() - test_runner.run(tests) diff --git a/unittest_to_pytest/repo-before/tests/__init__.py b/unittest_to_pytest/repo-before/tests/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/unittest_to_pytest/repo-before/tests/test_classes.py b/unittest_to_pytest/repo-before/tests/test_classes.py deleted file mode 100644 index c8de599..0000000 --- a/unittest_to_pytest/repo-before/tests/test_classes.py +++ /dev/null @@ -1,90 +0,0 @@ -# tests/test_classes.py - -import unittest -from unittest.mock import Mock -from jj_classes.castle import Castle -from jj_classes.character import Character - - -class TestCastle(unittest.TestCase): - """Tests for the Castle class.""" - - def setUp(self): - """Set up a test castle.""" - self.castle = Castle("Test Castle") - - def test_castle_name(self): - """Test that the castle name is set correctly.""" - self.assertEqual(self.castle.name, "Test Castle") - - def test_castle_boss(self): - """Test that the default boss is Bowser.""" - self.assertEqual(self.castle.boss, "Bowser") - - def test_castle_world(self): - """Test that the default world is Grass Land.""" - self.assertEqual(self.castle.world, "Grass Land") - - def test_has_access_granted(self): - """Test that access is granted for the correct powerup.""" - character = Mock(powerup="Super Mushroom") - self.assertTrue(self.castle.has_access(character)) - - def test_has_access_denied(self): - """Test that access is denied for an incorrect powerup.""" - character = Mock(powerup="Starman") - self.assertFalse(self.castle.has_access(character)) - - def test_empty_name_raises_error(self): - """Test that an empty castle name raises a ValueError.""" - with self.assertRaises(ValueError): - Castle("") - - -class TestCharacter(unittest.TestCase): - """Tests for the Character class.""" - - def setUp(self): - """Set up a test character.""" - self.character = Character("Mario") - - def test_character_name(self): - """Test that the character name is set correctly.""" - self.assertEqual(self.character.name, "Mario") - - def test_default_powerup(self): - """Test that the default powerup is None.""" - self.assertIsNone(self.character.powerup) - - def test_set_powerup(self): - """Test setting a powerup.""" - self.character.powerup = "Fire Flower" - self.assertEqual(self.character.powerup, "Fire Flower") - - def test_empty_name_raises_error(self): - """Test that an empty character name raises a ValueError.""" - with self.assertRaises(ValueError): - Character("") - - -class TestCastleAndCharacter(unittest.TestCase): - """Tests for the interaction between Castle and Character.""" - - def setUp(self): - """Set up a test castle and character.""" - self.castle = Castle("Test Castle") - self.character = Character("Mario") - - def test_character_has_access(self): - """Test that a character with the correct powerup has access.""" - self.character.powerup = "Super Mushroom" - self.assertTrue(self.castle.has_access(self.character)) - - def test_character_denied_access(self): - """Test that a character with the wrong powerup is denied access.""" - self.character.powerup = "Starman" - self.assertFalse(self.castle.has_access(self.character)) - - -if __name__ == "__main__": - unittest.main() diff --git a/unittest_to_pytest/run.py b/unittest_to_pytest/run.py index b98ed61..9bd17c0 100644 --- a/unittest_to_pytest/run.py +++ b/unittest_to_pytest/run.py @@ -1,3 +1,4 @@ +import codegen from codegen import Codebase # Initialize codebase @@ -52,8 +53,8 @@ def {fixture_name}(): setup_method.remove() print(f"🗑️ Removed setUp method from class {cls.name}") - -def main(): +@codegen.function("unittest-to-pytest") +def run(codebase: Codebase): """Main function to run the unittest to pytest conversion""" print("🚀 Starting unittest to pytest conversion...") @@ -76,4 +77,4 @@ def main(): if __name__ == "__main__": - main() + run(codebase) From a2ba0dcdc42dbb9f6bfcb870a8cbb4b850e534cd Mon Sep 17 00:00:00 2001 From: kopekC <28070492+kopekC@users.noreply.github.com> Date: Mon, 27 Jan 2025 23:45:58 +0000 Subject: [PATCH 2/4] Automated pre-commit update --- freezegun_to_timemachine_migration/run.py | 29 ++++++++++++----------- python2_to_python3/run.py | 1 + unittest_to_pytest/run.py | 1 + 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/freezegun_to_timemachine_migration/run.py b/freezegun_to_timemachine_migration/run.py index cf8603f..b5e6bd1 100644 --- a/freezegun_to_timemachine_migration/run.py +++ b/freezegun_to_timemachine_migration/run.py @@ -1,6 +1,7 @@ import codegen from codegen import Codebase + @codegen.function("freezegun-to-timemachine") def run(codebase: Codebase): """Convert FreezeGun usage to TimeMachine in test files. @@ -19,36 +20,35 @@ def run(codebase: Codebase): # Update imports for imp in file.imports: - if imp.symbol_name and 'freezegun' in imp.source: - if imp.name == 'freeze_time': + if imp.symbol_name and "freezegun" in imp.source: + if imp.name == "freeze_time": # required due to Codegen limitations - imp.edit('from time_machine import travel') + imp.edit("from time_machine import travel") else: - imp.set_import_module('time_machine') + imp.set_import_module("time_machine") # Find all function calls in the file for fcall in file.function_calls: # Skip if not a freeze_time call - if 'freeze_time' not in fcall.source: + if "freeze_time" not in fcall.source: continue # Get original source and prepare new source new_source = fcall.source # Add tick parameter if not present - if not fcall.get_arg_by_parameter_name('tick'): - if new_source.endswith(')'): + if not fcall.get_arg_by_parameter_name("tick"): + if new_source.endswith(")"): new_source = new_source[:-1] - if not new_source.endswith('('): - new_source += ',' - new_source += ' tick=False)' + if not new_source.endswith("("): + new_source += "," + new_source += " tick=False)" # Replace freeze_time with travel - if '.' in new_source: - new_source = new_source.replace( - 'freeze_time', 'travel').replace('freezegun', 'time_machine') + if "." in new_source: + new_source = new_source.replace("freeze_time", "travel").replace("freezegun", "time_machine") else: - new_source = 'travel' + new_source[len('freeze_time'):] + new_source = "travel" + new_source[len("freeze_time") :] # Make single edit with complete changes fcall.edit(new_source) @@ -56,6 +56,7 @@ def run(codebase: Codebase): codebase.commit() print("✅ FreezeGun to TimeMachine conversion completed successfully!") + if __name__ == "__main__": codebase = Codebase.from_repo("getmoto/moto", commit="786a8ada7ed0c7f9d8b04d49f24596865e4b7901") run(codebase) diff --git a/python2_to_python3/run.py b/python2_to_python3/run.py index 40eb6d7..38c7586 100644 --- a/python2_to_python3/run.py +++ b/python2_to_python3/run.py @@ -115,6 +115,7 @@ def update_iterators(file): new_stmt = new_stmt.rstrip() + ")" stmt.edit(new_stmt) + @codegen.function("python2-to-python3") def run(): """Main function to run the Python 2 to 3 conversion""" diff --git a/unittest_to_pytest/run.py b/unittest_to_pytest/run.py index 9bd17c0..045bbe0 100644 --- a/unittest_to_pytest/run.py +++ b/unittest_to_pytest/run.py @@ -53,6 +53,7 @@ def {fixture_name}(): setup_method.remove() print(f"🗑️ Removed setUp method from class {cls.name}") + @codegen.function("unittest-to-pytest") def run(codebase: Codebase): """Main function to run the unittest to pytest conversion""" From a65af18b58c620bc65ecb2b1421425cdfcd489de Mon Sep 17 00:00:00 2001 From: Eduardo Pujol Date: Mon, 27 Jan 2025 18:50:32 -0500 Subject: [PATCH 3/4] fixes --- python2_to_python3/guide.md | 39 -------- python2_to_python3/run.py | 3 +- .../input_repo/jj_classes/__init__.py | 0 .../input_repo/jj_classes/castle.py | 28 ++++++ .../input_repo/jj_classes/character.py | 23 +++++ unittest_to_pytest/input_repo/run_tests.py | 9 ++ .../input_repo/tests/__init__.py | 0 .../input_repo/tests/test_classes.py | 90 +++++++++++++++++++ unittest_to_pytest/run.py | 4 +- 9 files changed, 154 insertions(+), 42 deletions(-) delete mode 100644 python2_to_python3/guide.md create mode 100644 unittest_to_pytest/input_repo/jj_classes/__init__.py create mode 100644 unittest_to_pytest/input_repo/jj_classes/castle.py create mode 100644 unittest_to_pytest/input_repo/jj_classes/character.py create mode 100644 unittest_to_pytest/input_repo/run_tests.py create mode 100644 unittest_to_pytest/input_repo/tests/__init__.py create mode 100644 unittest_to_pytest/input_repo/tests/test_classes.py diff --git a/python2_to_python3/guide.md b/python2_to_python3/guide.md deleted file mode 100644 index 164bf09..0000000 --- a/python2_to_python3/guide.md +++ /dev/null @@ -1,39 +0,0 @@ -## How to Migrate - -### Step 1: Convert Print Statements to Function Calls - -Use the codemod to convert print statements in Python 2 to function calls in Python 3. - -👉 [Run the Convert Print Statements to Function Calls Codemod](https://www.codegen.sh/preview/7583) - -### Step 2: Unicode to Str Conversion - -Use the codemod to update Unicode string handling to be compatible with Python 3. - -👉 [Run the Unicode to Str Conversion Codemod](https://www.codegen.sh/preview/7587) - -### Step 3: Update Dictionary Iteration - -Use the codemod to update dictionary iteration to use the view objects returned by `dict.keys()`, `dict.values()`, and `dict.items()` in Python 3. - -👉 [Run the Update Dictionary Iteration Codemod](https://www.codegen.sh/preview/7590) - -### Step 4: Modernize Exception Handling - -Use the codemod to update exception handling syntax to be compatible with Python 3. - -👉 [Run the Modernize Exception Handling Codemod](https://www.codegen.sh/preview/7589) - -### Step 5: Update Iterators - -Use the codemod to replace the `next()` method with `__next__()` in iterators to be compatible with Python 3. - -👉 [Run the Update Iterators Codemod](https://www.codegen.sh/preview/7595) - ---- - -## Need Help? - -If you encounter issues or have specific edge cases not addressed by the codemods, reach out to the Codegen support team or visit the [Codegen Documentation](https://www.codegen.sh/docs) for detailed guidance. - -Start your Python 3 migration today and enjoy the benefits of a cleaner, modern codebase! diff --git a/python2_to_python3/run.py b/python2_to_python3/run.py index 38c7586..1417c95 100644 --- a/python2_to_python3/run.py +++ b/python2_to_python3/run.py @@ -2,7 +2,6 @@ from codegen import Codebase # Initialize codebase -codebase = Codebase("./") # Define the target directory TARGET_DIR = "input_repo" @@ -151,4 +150,6 @@ def run(): if __name__ == "__main__": + codebase = Codebase("./") + run(codebase) diff --git a/unittest_to_pytest/input_repo/jj_classes/__init__.py b/unittest_to_pytest/input_repo/jj_classes/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/unittest_to_pytest/input_repo/jj_classes/castle.py b/unittest_to_pytest/input_repo/jj_classes/castle.py new file mode 100644 index 0000000..cb95a69 --- /dev/null +++ b/unittest_to_pytest/input_repo/jj_classes/castle.py @@ -0,0 +1,28 @@ +# jj_classes/castle.py + +class Castle: + """Defines the Castle class.""" + + def __init__(self, name): + """Initialize the castle.""" + if not name: + raise ValueError("Castle name cannot be empty.") + self._name = name + self._boss = "Bowser" + self._world = "Grass Land" + + def has_access(self, character): + """Check if a character has access to the castle.""" + return character.powerup == "Super Mushroom" + + @property + def name(self): + return self._name + + @property + def boss(self): + return self._boss + + @property + def world(self): + return self._world diff --git a/unittest_to_pytest/input_repo/jj_classes/character.py b/unittest_to_pytest/input_repo/jj_classes/character.py new file mode 100644 index 0000000..53226be --- /dev/null +++ b/unittest_to_pytest/input_repo/jj_classes/character.py @@ -0,0 +1,23 @@ +# jj_classes/character.py + +class Character: + """Defines the Character class.""" + + def __init__(self, name): + """Initialize the character.""" + if not name: + raise ValueError("Character name cannot be empty.") + self._name = name + self._powerup = None + + @property + def name(self): + return self._name + + @property + def powerup(self): + return self._powerup + + @powerup.setter + def powerup(self, value): + self._powerup = value diff --git a/unittest_to_pytest/input_repo/run_tests.py b/unittest_to_pytest/input_repo/run_tests.py new file mode 100644 index 0000000..7417397 --- /dev/null +++ b/unittest_to_pytest/input_repo/run_tests.py @@ -0,0 +1,9 @@ +# run_tests.py + +import unittest + +if __name__ == "__main__": + loader = unittest.TestLoader() + tests = loader.discover("tests") + test_runner = unittest.TextTestRunner() + test_runner.run(tests) diff --git a/unittest_to_pytest/input_repo/tests/__init__.py b/unittest_to_pytest/input_repo/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/unittest_to_pytest/input_repo/tests/test_classes.py b/unittest_to_pytest/input_repo/tests/test_classes.py new file mode 100644 index 0000000..c8de599 --- /dev/null +++ b/unittest_to_pytest/input_repo/tests/test_classes.py @@ -0,0 +1,90 @@ +# tests/test_classes.py + +import unittest +from unittest.mock import Mock +from jj_classes.castle import Castle +from jj_classes.character import Character + + +class TestCastle(unittest.TestCase): + """Tests for the Castle class.""" + + def setUp(self): + """Set up a test castle.""" + self.castle = Castle("Test Castle") + + def test_castle_name(self): + """Test that the castle name is set correctly.""" + self.assertEqual(self.castle.name, "Test Castle") + + def test_castle_boss(self): + """Test that the default boss is Bowser.""" + self.assertEqual(self.castle.boss, "Bowser") + + def test_castle_world(self): + """Test that the default world is Grass Land.""" + self.assertEqual(self.castle.world, "Grass Land") + + def test_has_access_granted(self): + """Test that access is granted for the correct powerup.""" + character = Mock(powerup="Super Mushroom") + self.assertTrue(self.castle.has_access(character)) + + def test_has_access_denied(self): + """Test that access is denied for an incorrect powerup.""" + character = Mock(powerup="Starman") + self.assertFalse(self.castle.has_access(character)) + + def test_empty_name_raises_error(self): + """Test that an empty castle name raises a ValueError.""" + with self.assertRaises(ValueError): + Castle("") + + +class TestCharacter(unittest.TestCase): + """Tests for the Character class.""" + + def setUp(self): + """Set up a test character.""" + self.character = Character("Mario") + + def test_character_name(self): + """Test that the character name is set correctly.""" + self.assertEqual(self.character.name, "Mario") + + def test_default_powerup(self): + """Test that the default powerup is None.""" + self.assertIsNone(self.character.powerup) + + def test_set_powerup(self): + """Test setting a powerup.""" + self.character.powerup = "Fire Flower" + self.assertEqual(self.character.powerup, "Fire Flower") + + def test_empty_name_raises_error(self): + """Test that an empty character name raises a ValueError.""" + with self.assertRaises(ValueError): + Character("") + + +class TestCastleAndCharacter(unittest.TestCase): + """Tests for the interaction between Castle and Character.""" + + def setUp(self): + """Set up a test castle and character.""" + self.castle = Castle("Test Castle") + self.character = Character("Mario") + + def test_character_has_access(self): + """Test that a character with the correct powerup has access.""" + self.character.powerup = "Super Mushroom" + self.assertTrue(self.castle.has_access(self.character)) + + def test_character_denied_access(self): + """Test that a character with the wrong powerup is denied access.""" + self.character.powerup = "Starman" + self.assertFalse(self.castle.has_access(self.character)) + + +if __name__ == "__main__": + unittest.main() diff --git a/unittest_to_pytest/run.py b/unittest_to_pytest/run.py index 045bbe0..b4e32a5 100644 --- a/unittest_to_pytest/run.py +++ b/unittest_to_pytest/run.py @@ -2,10 +2,9 @@ from codegen import Codebase # Initialize codebase -codebase = Codebase("./") # Define the target directory -TARGET_DIR = "repo-before/tests" +TARGET_DIR = "input_repo/tests" def remove_unittest_inheritance(file): @@ -78,4 +77,5 @@ def run(codebase: Codebase): if __name__ == "__main__": + codebase = Codebase("./") run(codebase) From c8c44b0e76fef02774f36170eb0b39bab19ec064 Mon Sep 17 00:00:00 2001 From: kopekC <28070492+kopekC@users.noreply.github.com> Date: Mon, 27 Jan 2025 23:51:07 +0000 Subject: [PATCH 4/4] Automated pre-commit update --- unittest_to_pytest/input_repo/jj_classes/castle.py | 1 + unittest_to_pytest/input_repo/jj_classes/character.py | 1 + 2 files changed, 2 insertions(+) diff --git a/unittest_to_pytest/input_repo/jj_classes/castle.py b/unittest_to_pytest/input_repo/jj_classes/castle.py index cb95a69..7812ab3 100644 --- a/unittest_to_pytest/input_repo/jj_classes/castle.py +++ b/unittest_to_pytest/input_repo/jj_classes/castle.py @@ -1,5 +1,6 @@ # jj_classes/castle.py + class Castle: """Defines the Castle class.""" diff --git a/unittest_to_pytest/input_repo/jj_classes/character.py b/unittest_to_pytest/input_repo/jj_classes/character.py index 53226be..30edf8b 100644 --- a/unittest_to_pytest/input_repo/jj_classes/character.py +++ b/unittest_to_pytest/input_repo/jj_classes/character.py @@ -1,5 +1,6 @@ # jj_classes/character.py + class Character: """Defines the Character class."""