Migracja do nowego silnika Python-to-.NET w wersji 26.8
Wprowadzenie
Wersja 26.8 zastępuje silnik łączący Pythona z .NET. Podstawowe elementy rysowania zostały przeniesione do modułu aspose.slides.
Przejdź od razu do Mam błąd, jeśli po aktualizacji wystąpią problemy.
Podstawowe elementy rysowania przeniesione do aspose.slides
Przeniesiono siedem typów. Zachowują one swoje nazwy, argumenty i zachowanie:
| Typ | Przed 26.8 | 26.8 i później |
|---|---|---|
| Point | aspose.pydrawing.Point |
aspose.slides.Point |
| PointF | aspose.pydrawing.PointF |
aspose.slides.PointF |
| Size | aspose.pydrawing.Size |
aspose.slides.Size |
| SizeF | aspose.pydrawing.SizeF |
aspose.slides.SizeF |
| Rectangle | aspose.pydrawing.Rectangle |
aspose.slides.Rectangle |
| RectangleF | aspose.pydrawing.RectangleF |
aspose.slides.RectangleF |
| Color | aspose.pydrawing.Color |
aspose.slides.Color |
Te siedem typów stanowiło całą pozostałą zawartość aspose.pydrawing. Po przekierowaniu ich nie musisz już nigdzie w kodzie odwoływać się do aspose.pydrawing; wszystkie jego importy mogą zostać usunięte. To także ułatwia weryfikację – zobacz Zweryfikuj migrację.
Kod starszy:
import aspose.slides as slides
import aspose.pydrawing as drawing
with slides.Presentation() as presentation:
slide = presentation.slides[0]
shape = slide.shapes.add_auto_shape(slides.ShapeType.RECTANGLE, 50, 50, 200, 100)
shape.fill_format.fill_type = slides.FillType.SOLID
shape.fill_format.solid_fill_color.color = drawing.Color.red
with slide.get_image(drawing.Size(1920, 1080)) as slide_image:
slide_image.save("slide1.jpeg", slides.ImageFormat.JPEG)
Wersja 26.8:
import aspose.slides as slides
with slides.Presentation() as presentation:
slide = presentation.slides[0]
shape = slide.shapes.add_auto_shape(slides.ShapeType.RECTANGLE, 50, 50, 200, 100)
shape.fill_format.fill_type = slides.FillType.SOLID
shape.fill_format.solid_fill_color.color = slides.Color.red
with slide.get_image(slides.Size(1920, 1080)) as slide_image:
slide_image.save("slide1.jpeg", slides.ImageFormat.JPEG)
Forma importu from zmienia się w ten sam sposób:
# Stary kod
from aspose.pydrawing import Color, Point
# Wersja 26.8
from aspose.slides import Color, Point
Napraw błąd importu
Znajdź traceback w pierwszej kolumnie.
| Błąd | Przyczyna | Rozwiązanie |
|---|---|---|
AttributeError: module 'aspose.pydrawing' has no attribute 'Color' (or Point, Rectangle, and so on) |
Pakiet jest w wersji 26.8, a kod nadal wskazuje na stary moduł | Zaktualizuj swój kod |
ImportError: cannot import name 'Color' from 'aspose.pydrawing' |
Ta sama przyczyna, forma importu from |
Zaktualizuj swój kod |
ModuleNotFoundError: No module named 'aspose.pydrawing' |
Moduł i wszystkie jego siedem typów zostały przeniesione do aspose.slides |
Zaktualizuj swój kod, a następnie usuń import aspose.pydrawing |
ImportError: cannot import name 'Color' from 'aspose.slides' |
Kod został zmigrowany, ale zainstalowany pakiet jest wersją 26.7 lub starszą | pip install --upgrade aspose.slides |
TypeError on a color, point, or size argument |
Wartość utworzona z aspose.pydrawing została przekazana do nowego API |
Utwórz wartość również z aspose.slides |
Zaktualizuj swój kod
Ponieważ aspose.pydrawing nie zawiera nic oprócz siedmiu przeniesionych typów, migracja polega jedynie na zmianie nazwy modułu. Wszystkie formy importu są obsługiwane przez tę jedną zmianę, włącznie z aliasami:
# Stary kod
import aspose.pydrawing as drawing
color = drawing.Color.red
# Wersja 26.8 - alias nadal działa
import aspose.slides as drawing
color = drawing.Color.red
Jest to poprawne w dowolnym zakresie, w tym wewnątrz ciała funkcji, ponieważ alias pozostaje związany dokładnie tam, gdzie był wcześniej. Jedyną wadą jest myląca nazwa, więc rozważ wyraźne zaznaczenie intencji:
import aspose.slides as slides
color = slides.Color.red
Wybierz podejście, które odpowiada rozmiarowi Twojej bazy kodu.
Zastąp ręcznie
Dla kilku plików wyszukaj aspose.pydrawing i zamień go na aspose.slides, a następnie usuń niepotrzebne importy.
Zastąp za pomocą polecenia powłoki
Jest to zamiana zwykłego tekstu, więc wpływa także na wystąpienia w ciągach znaków i komentarzach. Oba polecenia zapisują kopię .bak każdego zmienianego pliku.
Linux:
grep -rlZ --include='*.py' 'aspose\.pydrawing' . \
| xargs -0 -r sed -i.bak 's/aspose\.pydrawing/aspose.slides/g'
W macOS użyj sed -i '' zamiast sed -i.bak lub zainstaluj GNU sed jako gsed.
Windows PowerShell:
Get-ChildItem -Recurse -Filter *.py | ForEach-Object {
$t = Get-Content $_ -Raw
$new = $t -replace 'aspose\.pydrawing', 'aspose.slides'
if ($new -ne $t) {
Copy-Item $_.FullName "$($_.FullName).bak"
Set-Content $_.FullName $new -NoNewline
$_.FullName
}
}
Aby cofnąć zmiany w Linux lub macOS:
find . -name '*.py.bak' -exec sh -c 'mv "$1" "${1%.bak}"' _ {} \;
Aby cofnąć zmiany w Windows:
Get-ChildItem -Recurse -Filter *.py.bak | ForEach-Object {
Move-Item $_.FullName ($_.FullName -replace '\.bak$', '') -Force
}
Zastąp skryptem Python
Ta sama zmiana nazwy, przenośna między Linux, macOS i Windows. Skrypt przyjmuje ścieżkę jako argument i wyświetla podgląd zmian, chyba że podano --write. Dodaj --backup, aby zachować kopię .bak każdego zmienionego pliku. Zapisz go pod dowolną nazwą – komunikat użycia pobiera nazwę w czasie uruchomienia.
"""Zmień nazwę aspose.pydrawing na aspose.slides. Zastąpienie zwykłym tekstem.
python <this script> src/ # podgląd
python <this script> src/ --write # zastosuj
python <this script> src/ --write --backup # zastosuj, zachowując kopie .bak
"""
import sys
from pathlib import Path
W = "--write" in sys.argv
B = "--backup" in sys.argv
ROOT = next((a for a in sys.argv[1:] if not a.startswith("-")), None)
if ROOT is None:
sys.exit(f"usage: python {Path(sys.argv[0]).name} <path> [--write] [--backup]")
root = Path(ROOT)
if not root.exists():
sys.exit(f"no such path: {root}")
files = [root] if root.is_file() else root.rglob("*.py")
changed = 0
for p in files:
if {".venv", "venv", "__pycache__", ".git"} & set(p.parts):
continue
s = p.read_text(encoding="utf-8")
n = s.replace("aspose.pydrawing", "aspose.slides")
if n == s:
continue
changed += 1
print(("wrote " if W else "would change ") + str(p))
if W:
if B:
p.with_suffix(p.suffix + ".bak").write_text(s, encoding="utf-8")
p.write_text(n, encoding="utf-8")
print(f"{changed} file(s) {'changed' if W else 'to change'}"
+ ("" if W or not changed else "; rerun with --write to apply"))
Przykładowe uruchomienie wygląda tak:
$ python migrate.py src/
would change src/render.py
would change src/export/slides.py
2 file(s) to change; rerun with --write to apply
$ python migrate.py src/ --write --backup
wrote src/render.py
wrote src/export/slides.py
2 file(s) changed
Ścieżka może być katalogiem, który jest przeszukiwany rekurencyjnie, lub pojedynczym plikiem .py.
Zastąp skryptem opartym na AST
Zalecane dla większych baz kodu. Skrypt wykonuje tę samą zmianę nazwy, ale najpierw parsuje każdy plik, dzięki czemu nie modyfikuje wystąpień w ciągach, komentarzach ani docstringach.
Ponieważ zmienia nazwę modułu w miejscu i pozostawia aliasy, wszystkie formy importu są obsługiwane bez specjalnych przypadków: import aspose.pydrawing, import aspose.pydrawing as X, from aspose.pydrawing import Color, from aspose.pydrawing import Color as C, wieloliniowe importy w nawiasach, importy wewnątrz funkcji oraz przekazywanie modułu jako wartości. Akceptuje te same flagi --write i --backup.
"""Rename aspose.pydrawing to aspose.slides, skipping strings and comments.
python <this script> src/ # preview
python <this script> src/ --write # apply
python <this script> src/ --write --backup # apply, keeping .bak copies
"""
import ast, sys
from pathlib import Path
MOD, DST = "aspose.pydrawing", "aspose.slides"
W = "--write" in sys.argv
B = "--backup" in sys.argv
ROOT = next((a for a in sys.argv[1:] if not a.startswith("-")), None)
if ROOT is None:
sys.exit(f"usage: python {Path(sys.argv[0]).name} <path> [--write] [--backup]")
root = Path(ROOT)
if not root.exists():
sys.exit(f"no such path: {root}")
files = [root] if root.is_file() else root.rglob("*.py")
changed = 0
def chain(n):
p = []
while isinstance(n, ast.Attribute):
p.append(n.attr)
n = n.value
return ".".join(reversed(p + [n.id])) if isinstance(n, ast.Name) else None
def fix(src):
tree = ast.parse(src)
off, o = [], 0
for l in src.encode().splitlines(keepends=True):
off.append(o)
o += len(l)
off.append(o)
edits = []
for n in ast.walk(tree):
# import aspose.pydrawing [as X] / from aspose.pydrawing import ...
# Nazwa modułu jest zmieniana w miejscu, więc każdy alias pozostaje związany tak jak wcześniej.
if (isinstance(n, ast.Import) and any(a.name == MOD for a in n.names)) or \
(isinstance(n, ast.ImportFrom) and n.module == MOD):
s, e = off[n.lineno - 1], off[n.end_lineno - 1] + n.end_col_offset
edits.append((s, e, src.encode()[s:e].decode().replace(MOD, DST)))
# Każde wyrażenie odnoszące się do modułu, w tym surowe `fn(aspose.pydrawing)`.
elif isinstance(n, ast.Attribute) and chain(n) == MOD:
edits.append((off[n.lineno - 1] + n.col_offset,
off[n.end_lineno - 1] + n.end_col_offset, DST))
b = src.encode()
for s, e, r in sorted(edits, reverse=True): # od końca do początku zachowuje poprawność offsetów
b = b[:s] + r.encode() + b[e:]
return b.decode()
for p in files:
if {".venv", "venv", "__pycache__", ".git"} & set(p.parts):
continue
s = p.read_text(encoding="utf-8")
try:
n = fix(s)
except SyntaxError as e:
print(f"skipped {p}: {e}")
continue
if n != s:
print(("wrote " if W else "would change ") + str(p))
if W:
if B:
p.with_suffix(p.suffix + ".bak").write_text(s, encoding="utf-8")
p.write_text(n, encoding="utf-8")
Oba skrypty są idempotentne: ponowne uruchomienie ich na już zmigrowanym kodzie nie wprowadza zmian.
Zweryfikuj migrację
Wyszukiwanie tekstowe pokazuje, czy coś pozostało:
grep -rn 'aspose\.pydrawing' --include='*.py' --exclude-dir=.venv .
Jest to szybkie, ale dopasowuje także wystąpienia w ciągach i komentarzach, więc czysty kod może nadal generować wyniki. Aby uzyskać ostateczną odpowiedź, użyj poniższego sprawdzenia. Raportuje jedynie rzeczywiste odwołania w kodzie i kończy działanie z niezerowym kodem wyjścia, jeśli coś pozostanie, co umożliwia użycie go jako bramki w procesie budowania.
import ast, sys
from pathlib import Path
MOD = "aspose.pydrawing"
ROOT = next((a for a in sys.argv[1:] if not a.startswith("-")), ".")
def chain(n):
p = []
while isinstance(n, ast.Attribute):
p.append(n.attr)
n = n.value
return ".".join(reversed(p + [n.id])) if isinstance(n, ast.Name) else None
def scan(tree):
for n in ast.walk(tree):
if isinstance(n, ast.Import) and any(a.name == MOD for a in n.names):
yield n.lineno, f"import {MOD}"
elif isinstance(n, ast.ImportFrom) and n.module == MOD:
names = ", ".join(a.name for a in n.names)
yield n.lineno, f"from {MOD} import {names}"
elif isinstance(n, ast.Attribute) and chain(n) == MOD:
yield n.lineno, f"reference to {MOD}"
hits = 0
for p in sorted(Path(ROOT).rglob("*.py")):
if {".venv", "venv", "__pycache__", ".git"} & set(p.parts):
continue
try:
tree = ast.parse(p.read_text(encoding="utf-8"))
except SyntaxError as e:
print(f"skipped {p}: {e}")
continue
for lineno, what in sorted(scan(tree)):
print(f"{p}:{lineno}: {what}")
hits += 1
print("migration complete" if not hits else f"{hits} reference(s) left")
sys.exit(1 if hits else 0)
Uruchom go przed i po migracji:
$ python verify.py src/
src/render.py:4: from aspose.pydrawing import Color, Point
src/render.py:11: import aspose.pydrawing
src/render.py:12: reference to aspose.pydrawing
3 reference(s) left
$ python migrate.py src/ --write
wrote src/render.py
$ python verify.py src/
migration complete
Na koniec uruchom test weryfikacyjny, który wykorzystuje przeniesione typy:
import aspose.slides as slides
with slides.Presentation() as presentation:
slide = presentation.slides[0]
shape = slide.shapes.add_auto_shape(slides.ShapeType.RECTANGLE, 50, 50, 200, 100)
shape.fill_format.fill_type = slides.FillType.SOLID
shape.fill_format.solid_fill_color.color = slides.Color.red
presentation.save("smoke.pptx", slides.export.SaveFormat.PPTX)
print("OK")
Zalecana kolejność migracji
- Zapisz punkt odniesienia. Uruchom testy na bieżącej wersji i zachowaj referencyjne renderingi. To pozwala później odróżnić błędy migracji od różnic w renderowaniu.
- Podgląd migracji. Uruchom jeden ze skryptów bez
--writei przejrzyj listę plików, które miałby zmienić. - Zastosuj i zweryfikuj. Uruchom z
--write --backup, a następnie skrypt weryfikacyjny i test weryfikacyjny. - Porównaj renderingi z tolerancją. Przejście na kompilację .NET 6 może wprowadzić małe różnice w tekście i efektach. Użyj porównania opartego na progach zamiast porównania bajt po bajcie.
- Usuń kopie zapasowe. Gdy wynik zostanie potwierdzony, usuń pliki
.bak:find . -name '*.py.bak' -deletew Linux i macOS lubGet-ChildItem -Recurse -Filter *.py.bak | Remove-Itemw Windows.
Obsługa obu wersji w jednej bazie kodu
Jeśli musisz uruchamiać kod przeciwko wersjom 26.7 i 26.8 z tego samego źródła:
try:
from aspose.slides import Color, Point, Rectangle # 26.8 i później
except ImportError:
from aspose.pydrawing import Color, Point, Rectangle # 26.7 i wcześniejsze
Co się nie zmieniło
- Nazwy, argumenty i zachowanie przeniesionych podstawowych elementów.
- Reszta interfejsu API
aspose.slides. - Licencjonowanie i sposób stosowania pliku licencji.
- Formaty plików oraz zachowanie przy zapisywaniu i wczytywaniu.
- Wymagania systemowe na Windows i macOS.
- Brak osobnej instalacji .NET – środowisko uruchomieniowe nadal jest w pakiecie.