-
Notifications
You must be signed in to change notification settings - Fork 27
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
30 changed files
with
1,309 additions
and
1,268 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
name: CI | ||
|
||
on: | ||
push: | ||
branches: | ||
- main | ||
pull_request: {} | ||
|
||
jobs: | ||
lint: | ||
runs-on: ubuntu-latest | ||
|
||
steps: | ||
- uses: actions/checkout@v2 | ||
|
||
- name: set up python | ||
uses: actions/setup-python@v4 | ||
with: | ||
python-version: "3.10" | ||
|
||
- name: Install hatch | ||
run: pip install hatch | ||
|
||
- uses: pre-commit/[email protected] | ||
with: | ||
extra_args: --all-files |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
repos: | ||
- repo: https://github.com/pre-commit/pre-commit-hooks | ||
rev: v4.3.0 | ||
hooks: | ||
- id: check-yaml | ||
args: ['--unsafe'] | ||
- id: check-toml | ||
- id: end-of-file-fixer | ||
- id: trailing-whitespace | ||
|
||
- repo: local | ||
hooks: | ||
- id: lint | ||
name: Lint | ||
entry: hatch run lint | ||
types: [python] | ||
language: system | ||
pass_filenames: false |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,31 +1,58 @@ | ||
# bump-pydantic | ||
# Bump Pydantic ♻️ | ||
|
||
[](https://pypi.org/project/bump-pydantic) | ||
[](https://pypi.org/project/bump-pydantic) | ||
<!-- [](https://pypi.org/project/bump-pydantic) | ||
[](https://pypi.org/project/bump-pydantic) --> | ||
|
||
Utility to bump pydantic from V1 to V2. | ||
|
||
----- | ||
|
||
**Table of Contents** | ||
### Rules | ||
|
||
#### BP001: Replace imports | ||
|
||
- ✅ Replace `BaseSettings` from `pydantic` to `pydantic_settings`. | ||
- ✅ Replace `Color` and `PaymentCardNumber` from `pydantic` to `pydantic_extra_types`. | ||
|
||
#### BP002: Add default `None` to `Optional[T]`, `Union[T, None]` and `Any` fields | ||
|
||
- ✅ Add default `None` to `Optional[T]` fields. | ||
|
||
The following code will be transformed: | ||
|
||
- [bump-pydantic](#bump-pydantic) | ||
- [Installation](#installation) | ||
- [Usage](#usage) | ||
- [License](#license) | ||
```py | ||
class User(BaseModel): | ||
name: Optional[str] | ||
``` | ||
|
||
## Installation | ||
Into: | ||
|
||
```console | ||
pip install bump-pydantic | ||
```py | ||
class User(BaseModel): | ||
name: Optional[str] = None | ||
``` | ||
|
||
## Usage | ||
#### BP003: Replace `Config` class by `model_config` | ||
|
||
- ✅ Replace `Config` class by `model_config = ConfigDict()`. | ||
|
||
The following code will be transformed: | ||
|
||
You can run `bump-pydantic` from the command line: | ||
```py | ||
class User(BaseModel): | ||
name: str | ||
|
||
```console | ||
bump-pydantic <FILES> | ||
class Config: | ||
extra = 'forbid' | ||
``` | ||
|
||
## License | ||
Into: | ||
|
||
```py | ||
class User(BaseModel): | ||
name: str | ||
|
||
model_config = ConfigDict(extra='forbid') | ||
``` | ||
|
||
`bump-pydantic` is distributed under the terms of the [MIT](https://spdx.org/licenses/MIT.html) license. | ||
#### BP004: Replace `BaseModel` methods |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,98 +1,4 @@ | ||
import difflib | ||
import os | ||
import sys | ||
import time | ||
from pathlib import Path | ||
|
||
import libcst as cst | ||
from libcst.codemod import CodemodContext | ||
from libcst.helpers import calculate_module_and_package | ||
from libcst.metadata import FullRepoManager, PositionProvider, ScopeProvider | ||
from libcst_mypy import MypyTypeInferenceProvider | ||
from typer import Argument, Option, Typer | ||
|
||
from bump_pydantic.transformers import gather_transformers | ||
|
||
app = Typer(help="Convert Pydantic from V1 to V2.") | ||
|
||
|
||
@app.command() | ||
def main( | ||
package: Path = Argument(..., exists=True, dir_okay=True, allow_dash=False), | ||
diff: bool = Option(False, help="Show diff instead of applying changes."), | ||
debug: bool = Option(False, help="Show debug logs."), | ||
add_default_none: bool = True, | ||
# NOTE: It looks like there are some issues with the libcst.codemod.RenameCommand. | ||
# To replicate the issue: clone aiopenapi3, and run `python -m bump_pydantic aiopenapi3`. | ||
# For that reason, the default is False. | ||
rename_imports: bool = False, | ||
rename_methods: bool = True, | ||
replace_config_class: bool = True, | ||
replace_config_parameters: bool = True, | ||
) -> None: | ||
# sourcery skip: hoist-similar-statement-from-if, simplify-len-comparison, swap-nested-ifs | ||
files = [str(path.absolute()) for path in package.glob("**/*.py")] | ||
|
||
transformers = gather_transformers( | ||
add_default_none=add_default_none, | ||
rename_imports=rename_imports, | ||
rename_methods=rename_methods, | ||
replace_config_class=replace_config_class, | ||
replace_config_parameters=replace_config_parameters, | ||
) | ||
|
||
cwd = os.getcwd() | ||
providers = {MypyTypeInferenceProvider, ScopeProvider, PositionProvider} | ||
metadata_manager = FullRepoManager(cwd, files, providers=providers) | ||
print("Inferring types... This may take a while.") | ||
metadata_manager.resolve_cache() | ||
print("Types are inferred.") | ||
|
||
start_time = time.time() | ||
|
||
# TODO: We can run this in parallel - batch it into files / cores. | ||
# We may need to run the resolve_cache() on each core - not sure. | ||
for transformer in transformers: | ||
for filename in files: | ||
module_and_package = calculate_module_and_package(cwd, filename) | ||
transform = transformer( | ||
CodemodContext( | ||
metadata_manager=metadata_manager, | ||
filename=filename, | ||
full_module_name=module_and_package.name, | ||
full_package_name=module_and_package.package, | ||
) | ||
) | ||
if debug: | ||
print(f"Processing {filename} with {transform.__class__.__name__}") | ||
|
||
with open(filename) as fp: | ||
old_code = fp.read() | ||
|
||
input_tree = cst.parse_module(old_code) | ||
output_tree = transform.transform_module(input_tree) | ||
|
||
input_code = input_tree.code | ||
output_code = output_tree.code | ||
|
||
if input_code != output_code: | ||
if diff: | ||
# TODO: Should be colored. | ||
lines = difflib.unified_diff( | ||
input_code.splitlines(keepends=True), | ||
output_code.splitlines(keepends=True), | ||
fromfile=filename, | ||
tofile=filename, | ||
) | ||
sys.stdout.writelines(lines) | ||
else: | ||
with open(filename, "w") as fp: | ||
fp.write(output_tree.code) | ||
|
||
modified = [Path(f) for f in files if os.stat(f).st_mtime > start_time] | ||
if len(modified) > 0: | ||
print(f"Refactored {len(modified)} files.") | ||
|
||
from bump_pydantic.main import app | ||
|
||
if __name__ == "__main__": | ||
app() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
from typing import List, Type | ||
|
||
from libcst.codemod import ContextAwareTransformer | ||
from libcst.codemod.visitors import AddImportsVisitor | ||
|
||
from bump_pydantic.codemods.add_default_none import AddDefaultNoneCommand | ||
from bump_pydantic.codemods.replace_config import ReplaceConfigCodemod | ||
from bump_pydantic.codemods.replace_imports import ReplaceImportsCodemod | ||
|
||
|
||
def gather_codemods() -> List[Type[ContextAwareTransformer]]: | ||
return [ | ||
AddDefaultNoneCommand, | ||
ReplaceConfigCodemod, | ||
ReplaceImportsCodemod, | ||
# AddImportsVisitor needs to be the last. | ||
AddImportsVisitor, | ||
] |
Oops, something went wrong.