Migrera till den nya Python-till-.NET-motorn i version 26.8

Introduktion

Version 26.8 ersätter motorn som kopplar Python till .NET. Rita‑primitiverna har flyttats till aspose.slides‑modulen.

Hoppa direkt till Jag har ett fel om du får problem efter uppgraderingen.

Ritprimitiver flyttade till aspose.slides

Sju typer har flyttats. De behåller sina namn, argument och beteende:

Typ Före 26.8 26.8 och senare
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

Dessa sju typer utgjorde hela återstående innehållet i aspose.pydrawing. När du har ompekat dem behöver ingen kod längre referera till aspose.pydrawing, och alla importeringar av den kan tas bort. Det gör också resultatet lätt att kontrollera – se Verifiera migreringen.

Gammal kod:

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)

Version 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)

from‑importformen ändras på samma sätt:

# Gammal kod
from aspose.pydrawing import Color, Point

# Version 26.8
from aspose.slides import Color, Point

Åtgärda ett importfel

Hitta din stackspårning i den första kolumnen.

Fel Orsak Åtgärd
AttributeError: module 'aspose.pydrawing' has no attribute 'Color' (eller Point, Rectangle o.s.v.) Paketet är 26.8, men koden pekar fortfarande på den gamla modulen Uppdatera koden
ImportError: cannot import name 'Color' from 'aspose.pydrawing' Samma orsak, from‑importform Uppdatera koden
ModuleNotFoundError: No module named 'aspose.pydrawing' Modulen och alla dess sju typer har flyttats till aspose.slides Uppdatera koden, ta sedan bort aspose.pydrawing‑importen
ImportError: cannot import name 'Color' from 'aspose.slides' Koden var migrerad, men det installerade paketet är 26.7 eller äldre pip install --upgrade aspose.slides
TypeError on a color, point, or size argument Ett värde skapat från aspose.pydrawing skickas till det nya API:t Skapa värdet också från aspose.slides

Uppdatera din kod

Eftersom aspose.pydrawing inte har något innehåll förutom de sju flyttade typerna, är migreringen ett namnbyte av modulen. Varje importform hanteras av detta enkla namnbyte, inklusive alias:

# Gammal kod
import aspose.pydrawing as drawing
color = drawing.Color.red

# Version 26.8 - aliaset fungerar fortfarande
import aspose.slides as drawing
color = drawing.Color.red

Detta är giltigt i alla scoper, inklusive i en funktionskropp, eftersom aliaset förblir bundet exakt där det var bundet tidigare. Den enda nackdelen är ett missvisande namn, så överväg att göra avsikten explicit:

import aspose.slides as slides
color = slides.Color.red

Välj den metod som passar storleken på din kodbas.

Ersätt manuellt

För några filer, sök efter aspose.pydrawing och ersätt med aspose.slides, ta sedan bort eventuell import som inte längre behövs.

Ersätt med ett skal‑kommando

Detta är en raktextersättning, så den påverkar även förekomster i strängar och kommentarer. Båda kommandona skriver en .bak‑kopia av varje fil de ändrar.

Linux:

grep -rlZ --include='*.py' 'aspose\.pydrawing' . \
  | xargs -0 -r sed -i.bak 's/aspose\.pydrawing/aspose.slides/g'

På macOS, använd sed -i '' istället för sed -i.bak, eller installera GNU sed som 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
  }
}

För att återgå på Linux eller macOS:

find . -name '*.py.bak' -exec sh -c 'mv "$1" "${1%.bak}"' _ {} \;

För att återgå på Windows:

Get-ChildItem -Recurse -Filter *.py.bak | ForEach-Object {
  Move-Item $_.FullName ($_.FullName -replace '\.bak$', '') -Force
}

Ersätt med ett Python‑skript

Samma namnbyte, portabelt över Linux, macOS och Windows. Skriptet tar sökvägen som argument och visar en förhandsgranskning av ändringarna om inte --write anges. Lägg till --backup för att behålla en .bak‑kopia av varje ändrad fil. Spara det under valfritt namn – användningsmeddelandet hämtar namnet vid körning.

"""Rename aspose.pydrawing to aspose.slides. Plain text replacement.

    python <this script> src/                     # preview
    python <this script> src/ --write             # apply
    python <this script> src/ --write --backup    # apply, keeping .bak copies
"""

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"))

Ett typiskt körning ser ut så här:

$ 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

Vägen kan vara en katalog, som genomsöks rekursivt, eller en enskild .py‑fil.

Ersätt med ett AST‑baserat skript

Rekommenderas för större kodbaser. Detta skript utför samma namnbyte, men parsar först varje fil, så att det aldrig rör förekomster i strängar, kommentarer eller docstrings.

Eftersom det byter namn på modulen på plats och låter alias vara kvar, hanteras varje importform utan specialfall: import aspose.pydrawing, import aspose.pydrawing as X, from aspose.pydrawing import Color, from aspose.pydrawing import Color as C, flerradiga parenteserade importer, importer inuti funktioner och modulen som värde. Det accepterar samma --write‑ och --backup‑flaggor.

"""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):
        # importera aspose.pydrawing [som X]  /  från aspose.pydrawing import ...
        # Modulnamnet byts namn på plats, så att alla alias förblir bundna som tidigare.
        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)))
        # Alla uttryck som refererar till modulen, inklusive rena `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):  # back to front keeps offsets valid
        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")

Båda skripten är idempotenta: att köra dem igen på migrerad kod ändrar ingenting.

Verifiera migreringen

En textsökning visar om något återstår:

grep -rn 'aspose\.pydrawing' --include='*.py' --exclude-dir=.venv .

Detta är snabbt, men det matchar även i strängar och kommentarer, så ren kod kan ändå ge träffar. För ett definitivt svar, använd kontrollen nedan. Den rapporterar bara riktiga kodreferenser och avslutar med en icke‑noll status om något finns kvar, vilket gör den användbar som bygggrind.

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)

Kör den före och efter migreringen:

$ 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

Slutligen, kör ett röktest som använder de flyttade typerna:

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")

Rekommenderad migrationsordning

  1. Spara en baslinje. Kör dina tester på den aktuella versionen och behåll referensrenderingar. Detta låter dig separera migrationsfel från renderingsskillnader senare.
  2. Förhandsgranska migreringen. Kör ett av skripten utan --write och granska listan över filer som skulle ändras.
  3. Tillämpa och verifiera. Kör med --write --backup, sedan verifieringsskriptet och röktestet.
  4. Jämför renderingar med en tolerans. Övergången till .NET 6‑byggnaden kan ge små skillnader i text och effekter. Använd en tröskelbaserad jämförelse snarare än en exakt byte‑för‑byte‑kontroll.
  5. Ta bort säkerhetskopiorna. När resultatet är bekräftat, ta bort .bak‑filerna: find . -name '*.py.bak' -delete på Linux och macOS, eller Get-ChildItem -Recurse -Filter *.py.bak | Remove-Item på Windows.

Stöd båda versionerna i en kodbas

Om du behöver köra mot 26.7 och 26.8 från samma källa:

try:
    from aspose.slides import Color, Point, Rectangle      # 26.8 och senare
except ImportError:
    from aspose.pydrawing import Color, Point, Rectangle   # 26.7 och tidigare

Vad som inte ändrades

  • Namn, argument och beteende för de flyttade primitiva typerna.
  • Resten av aspose.slides‑API‑ytan.
  • Licensiering och hur licensfilen appliceras.
  • Filformat samt spar‑ och läsbeteende.
  • Systemkrav på Windows och macOS.
  • Avsaknaden av en separat .NET‑installation – körningsmiljön är fortfarande inbäddad.