Migrer vers le nouveau moteur Python-vers-.NET dans la version 26.8
Introduction
La version 26.8 remplace le moteur qui relie Python à .NET. Les primitives de dessin ont été déplacées dans le module aspose.slides.
Jump straight to I Have an Error if you have an issues after upgrade.
Primitives de dessin déplacées vers aspose.slides
Sept types ont été déplacés. Ils conservent leurs noms, arguments et comportement :
| Type | Avant 26.8 | 26.8 et versions ultérieures |
|---|---|---|
| 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 |
Ces sept types constituaient l’intégralité du contenu restant de aspose.pydrawing. Une fois que vous les avez repointés, votre code n’a plus besoin de faire référence à aspose.pydrawing, et chaque importation peut être supprimée. Cela rend également le résultat facile à vérifier – voir Verify the Migration.
Legacy code:
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)
La forme d’import from change de la même manière:
# Code hérité
from aspose.pydrawing import Color, Point
# Version 26.8
from aspose.slides import Color, Point
Corriger une erreur d’importation
Trouvez votre trace d’erreur dans la première colonne.
| Erreur | Cause | Correction |
|---|---|---|
AttributeError: module 'aspose.pydrawing' has no attribute 'Color' (or Point, Rectangle, and so on) |
Le package est en version 26.8, le code pointe toujours vers l’ancien module | Update your code |
ImportError: cannot import name 'Color' from 'aspose.pydrawing' |
La même cause, forme d’import from |
Update your code |
ModuleNotFoundError: No module named 'aspose.pydrawing' |
Le module et les sept types ont été déplacés dans aspose.slides |
Update your code, then delete the aspose.pydrawing import |
ImportError: cannot import name 'Color' from 'aspose.slides' |
Le code a été migré, mais le package installé est en version 26.7 ou antérieure | pip install --upgrade aspose.slides |
TypeError on a color, point, or size argument |
Une valeur créée à partir de aspose.pydrawing est transmise à la nouvelle API |
Create the value from aspose.slides as well |
Mettez à jour votre code
Comme aspose.pydrawing ne contient plus rien d’autre que les sept types déplacés, la migration consiste en un simple renommage du module. Chaque forme d’import est couverte par ce seul renommage, y compris les alias:
# Code hérité
import aspose.pydrawing as drawing
color = drawing.Color.red
# Version 26.8 - l'alias reste fonctionnel
import aspose.slides as drawing
color = drawing.Color.red
Ceci est valable dans n’importe quel scope, y compris à l’intérieur du corps d’une fonction, car l’alias reste lié exactement où il l’était auparavant. Le seul inconvénient est un nom trompeur, il peut donc être judicieux de rendre l’intention explicite:
import aspose.slides as slides
color = slides.Color.red
Choisissez l’approche qui correspond à la taille de votre base de code.
Remplacer manuellement
Pour quelques fichiers, recherchez aspose.pydrawing et remplacez-le par aspose.slides, puis supprimez tout import qui n’est plus nécessaire.
Remplacer avec une commande shell
Il s’agit d’un remplacement en texte brut, il affecte donc également les occurrences à l’intérieur des chaînes et des commentaires. Les deux commandes créent une copie .bak de chaque fichier modifié.
Linux:
grep -rlZ --include='*.py' 'aspose\.pydrawing' . \
| xargs -0 -r sed -i.bak 's/aspose\.pydrawing/aspose.slides/g'
Sur macOS, utilisez sed -i '' au lieu de sed -i.bak, ou installez GNU sed sous le nom 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
}
}
To roll back on Linux or macOS:
find . -name '*.py.bak' -exec sh -c 'mv "$1" "${1%.bak}"' _ {} \;
To roll back on Windows:
Get-ChildItem -Recurse -Filter *.py.bak | ForEach-Object {
Move-Item $_.FullName ($_.FullName -replace '\.bak$', '') -Force
}
Remplacer avec un script Python
Le même renommage, portable sur Linux, macOS et Windows. Le script prend le chemin en argument et prévisualise les changements sauf si --write est fourni. Ajoutez --backup pour conserver une copie .bak de chaque fichier modifié. Enregistrez-le sous n’importe quel nom ; le message d’utilisation récupère le nom au moment de l’exécution.
"""Renommer aspose.pydrawing en aspose.slides. Remplacement en texte brut.
python <this script> src/ # aperçu
python <this script> src/ --write # appliquer
python <this script> src/ --write --backup # appliquer, en conservant les copies .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"))
Un exemple d’exécution ressemble à ceci :
$ 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
Le chemin peut être un répertoire, parcouru récursivement, ou un fichier .py unique.
Remplacer avec un script basé sur l’AST
Recommandé pour les bases de code plus importantes. Ce script effectue le même renommage, mais analyse chaque fichier d’abord, de sorte qu’il ne touche jamais les occurrences dans les chaînes, les commentaires ou les docstrings.
Comme il renomme le module en place et laisse les alias intacts, chaque forme d’import est gérée sans cas particuliers : import aspose.pydrawing, import aspose.pydrawing as X, from aspose.pydrawing import Color, from aspose.pydrawing import Color as C, imports parenthésés sur plusieurs lignes, imports à l’intérieur de fonctions, et le module passé comme valeur. Il accepte les mêmes drapeaux --write et --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):
# importer aspose.pydrawing [as X] / depuis aspose.pydrawing import ...
# Le nom du module est renommé sur place, donc tout alias reste lié comme avant.
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)))
# Toute expression se référant au module, y compris une utilisation directe `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): # le traitement de la fin vers le début préserve la validité des offsets
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")
Les deux scripts sont idempotents : les exécuter à nouveau sur du code migré ne change rien.
Vérifier la migration
Une recherche textuelle montre s’il reste quelque chose :
grep -rn 'aspose\.pydrawing' --include='*.py' --exclude-dir=.venv .
C’est rapide, mais cela correspond aussi aux occurrences dans les chaînes et les commentaires, de sorte qu’un code propre peut encore produire des résultats. Pour une réponse définitive, utilisez la vérification ci‑dessous. Elle ne signale que les vraies références de code et renvoie un statut non nul s’il en reste, ce qui la rend utilisable comme porte de build.
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)
Exécutez‑la avant et après la migration :
$ 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
Enfin, lancez un test de fumée qui exerce les types déplacés :
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")
Ordre de migration recommandé
- Save a baseline. Run your tests on the current version and keep reference renders. This lets you separate migration errors from rendering differences later.
- Preview the migration. Run one of the scripts without
--writeand review the list of files it would change. - Apply and verify. Run with
--write --backup, then the verification script and the smoke test. - Compare renders with a tolerance. The move to the .NET 6 build may produce small differences in text and effects. Use a threshold‑based comparison rather than a byte‑for‑byte check.
- Remove the backups. Once the result is confirmed, delete the
.bakfiles:find . -name '*.py.bak' -deleteon Linux and macOS, orGet-ChildItem -Recurse -Filter *.py.bak | Remove-Itemon Windows.
Prendre en charge les deux versions dans une même base de code
If you need to run against 26.7 and 26.8 from the same source:
try:
from aspose.slides import Color, Point, Rectangle # 26.8 et versions ultérieures
except ImportError:
from aspose.pydrawing import Color, Point, Rectangle # 26.7 et versions antérieures
Ce qui n’a pas changé
- Noms, arguments et comportement des primitives déplacées.
- Le reste de la surface d’API
aspose.slides. - Licence et mode d’application du fichier de licence.
- Formats de fichiers ainsi que le comportement de sauvegarde et de chargement.
- Exigences système sur Windows et macOS.
- L’absence d’une installation .NET séparée – le runtime est toujours fourni avec le package.