Skip to content

Instantly share code, notes, and snippets.

@Kludex
Created April 17, 2023 10:11
Show Gist options
  • Save Kludex/9087acb5af2a940bd3fcefce96799412 to your computer and use it in GitHub Desktop.
Save Kludex/9087acb5af2a940bd3fcefce96799412 to your computer and use it in GitHub Desktop.
Scripts used to generate the list of objects on https://github.com/pydantic/pydantic/pull/5480
import importlib
import pkgutil
def get_public_objects(package_name, parent_module=""):
objects = []
full_package_name = (
parent_module + "." + package_name if parent_module else package_name
)
package = importlib.import_module(full_package_name)
for obj_name in getattr(package, "__all__", []):
objects.append(full_package_name + "." + obj_name)
for _, module_name, is_pkg in pkgutil.iter_modules(package.__path__):
if module_name.startswith("_"): # Skip private modules
continue
module_path = full_package_name + "." + module_name
if is_pkg:
objects.extend(
get_public_objects(module_name, parent_module=full_package_name)
)
else:
module = importlib.import_module(module_path)
for obj_name in getattr(module, "__all__", []):
objects.append(module_path + "." + obj_name)
return objects
# Example usage:
packages = ["pydantic"]
try:
import pydantic_extra_types
packages.append("pydantic_extra_types")
except ImportError:
pass
try:
import pydantic_settings
packages.append("pydantic_settings")
except ImportError:
pass
for package_name in packages:
public_objects = get_public_objects(package_name)
for obj in public_objects:
print(obj)
"""Find the difference between pydantic v1 and v2."""
import contextlib
import os
import subprocess
import tempfile
import venv
from typing import Iterator, Literal
from rich.console import Console
console = Console()
@contextlib.contextmanager
def create_python_exe() -> Iterator[str]:
"""Create a python executable.
Returns:
The path to the python executable.
"""
with tempfile.TemporaryDirectory() as temp_dir:
venv_path = os.path.join(temp_dir, "venv")
venv.create(venv_path, with_pip=True)
python_exe = os.path.join(venv_path, "bin", "python")
yield python_exe
def collect_python_objects(version: Literal["v1", "v2"]) -> set[str]:
"""Collect all objects from pydantic.
Args:
version (Literal["v1", "v2"]): The version of pydantic to install.
Returns:
set[str]: A set of tuples containing the object name and the module
name.
"""
with create_python_exe() as python_exe:
subprocess_args = [python_exe, "-m", "pip", "install"]
if version == "v2":
subprocess_args.extend(
[
"--pre",
"git+https://github.com/pydantic/pydantic-extra-types",
# "git+https://github.com/pydantic/pydantic-settings",
]
)
subprocess_args.extend(["pydantic", "stdlib-list", "mypy"])
subprocess.run(subprocess_args)
result = subprocess.run([python_exe, "collect.py"], capture_output=True)
return {dotted_path.decode() for dotted_path in result.stdout.splitlines()}
def list_moved(result_v1: set[str], result_v2: set[str]) -> None:
"""List all objects that have been moved from v1 to v2.
Args:
result_v1 (set[str]): The objects from v1.
result_v2 (set[str]): The objects from v2.
"""
moved: dict[str, str] = {}
for dotted_path_v1 in result_v1:
module_v1, obj_v1 = dotted_path_v1.rsplit(".", 1)
found_on_v2 = False
location: str | None = None
for dotted_path_v2 in result_v2:
module_v2, obj_v2 = dotted_path_v2.rsplit(".", 1)
if dotted_path_v1 == dotted_path_v2:
found_on_v2 = True
if obj_v1 == obj_v2 and module_v1 != module_v2:
location = dotted_path_v2
if not found_on_v2 and location:
moved[dotted_path_v1] = location
console.print(f"Moved objects: {len(moved)}", style="bold")
console.print(moved)
def list_removed(result_v1: set[str], result_v2: set[str]) -> None:
"""List all objects that have been removed from v1 to v2.
Args:
result_v1 (set[str]): The objects from v1.
result_v2 (set[str]): The objects from v2.
"""
removed: set[str] = set()
for dotted_path_v1 in result_v1:
_, obj_v1 = dotted_path_v1.rsplit(".", 1)
found_on_v2 = False
for dotted_path_v2 in result_v2:
_, obj_v2 = dotted_path_v2.rsplit(".", 1)
if obj_v1 == obj_v2:
found_on_v2 = True
if not found_on_v2:
removed.add(dotted_path_v1)
console.print(f"Removed objects: {len(removed)}", style="bold")
# sort the removed objects
console.print(sorted(removed))
if __name__ == "__main__":
result_v1 = collect_python_objects("v1")
result_v2 = collect_python_objects("v2")
# console.print(result_v1, style="red")
# console.print(result_v2, style="green")
list_moved(result_v1, result_v2)
list_removed(result_v1, result_v2)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment