[pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci
This commit is contained in:
pre-commit-ci[bot]
2025-09-22 13:50:52 +00:00
parent fee9d78fad
commit 5aadc9cd6c
8 changed files with 86 additions and 76 deletions

View File

@@ -31,6 +31,7 @@ else:
class Workbench:
pass
FreeCAD.__unit_test__ += ["TestCAMGui"]
@@ -65,7 +66,6 @@ class CAMWorkbench(Workbench):
self.__class__.MenuText = "CAM"
self.__class__.ToolTip = "CAM workbench"
def Initialize(self):
global PathCommandGroup
@@ -97,6 +97,7 @@ class CAMWorkbench(Workbench):
# Check if CAM asset migration is needed for version upgrade
from Path.Tool.migration.migration import CAMAssetMigrator
migrator = CAMAssetMigrator()
migrator.check_migration_needed()

View File

@@ -140,7 +140,9 @@ def getAssetPath() -> pathlib.Path:
# Migrate: Set the legacy path as the new CamAssets path
setAssetPath(legacy_path_obj)
# Return the most recent version of the legacy path
most_recent_legacy = FreeCAD.ApplicationDirectories.mostRecentConfigFromBase(str(legacy_path_obj))
most_recent_legacy = FreeCAD.ApplicationDirectories.mostRecentConfigFromBase(
str(legacy_path_obj)
)
return pathlib.Path(most_recent_legacy)
# Fallback to default if no legacy path found

View File

@@ -75,12 +75,12 @@ class AssetOpenDialog(QFileDialog):
raw_data = file_path.read_bytes()
dependencies = serializer_class.extract_dependencies(raw_data)
external_toolbits = [] # Track toolbits found externally
for dependency_uri in dependencies:
# First check if dependency exists in asset manager stores
if self.asset_manager.exists(dependency_uri, store=["local", "builtin"]):
continue
# If not in stores, check if it exists relative to the library file
dependency_found = False
if dependency_uri.asset_type == "toolbit":
@@ -88,8 +88,8 @@ class AssetOpenDialog(QFileDialog):
# Library is in Library/, toolbits are in parallel Bit/
library_dir = file_path.parent # e.g., /path/to/Library/
tools_dir = library_dir.parent # e.g., /path/to/
bit_dir = tools_dir / "Bit" # e.g., /path/to/Bit/
bit_dir = tools_dir / "Bit" # e.g., /path/to/Bit/
if bit_dir.exists():
possible_extensions = [".fctb", ".json", ".yaml", ".yml"]
for ext in possible_extensions:
@@ -98,7 +98,7 @@ class AssetOpenDialog(QFileDialog):
dependency_found = True
external_toolbits.append((dependency_uri, toolbit_file))
break
if not dependency_found:
QMessageBox.critical(
self,
@@ -106,22 +106,23 @@ class AssetOpenDialog(QFileDialog):
f"Failed to import {file_path}: required dependency {dependency_uri} not found in stores or in parallel Bit directory",
)
return None
# If we found external toolbits, ask user if they want to import them
if external_toolbits:
from PySide.QtGui import QMessageBox
toolbit_names = [uri.asset_id for uri, _ in external_toolbits]
reply = QMessageBox.question(
self,
"Import External Toolbits",
f"This library references {len(external_toolbits)} toolbit(s) that are not in your local store:\n\n" +
"\n".join(f"{name}" for name in toolbit_names) +
f"\n\nWould you like to import these toolbits into your local store?\n" +
"This will make them permanently available for use in other libraries.",
f"This library references {len(external_toolbits)} toolbit(s) that are not in your local store:\n\n"
+ "\n".join(f"{name}" for name in toolbit_names)
+ f"\n\nWould you like to import these toolbits into your local store?\n"
+ "This will make them permanently available for use in other libraries.",
QMessageBox.Yes | QMessageBox.No,
QMessageBox.Yes
QMessageBox.Yes,
)
if reply == QMessageBox.Yes:
# Import the external toolbits into local store
self._import_external_toolbits(external_toolbits)
@@ -139,13 +140,13 @@ class AssetOpenDialog(QFileDialog):
# Load and return the asset.
try:
# Always use context-aware deserialization for libraries to get meaningful names
if hasattr(serializer_class, 'deep_deserialize_with_context'):
if hasattr(serializer_class, "deep_deserialize_with_context"):
# Pass file path context for meaningful library names and external dependency resolution
asset = serializer_class.deep_deserialize_with_context(raw_data, file_path)
else:
# Fallback to regular deserialization
asset = serializer_class.deep_deserialize(raw_data)
if not isinstance(asset, self.asset_class):
raise TypeError(f"Deserialized asset is not of type {self.asset_class.__name__}")
return asset
@@ -185,10 +186,10 @@ class AssetOpenDialog(QFileDialog):
"""Import external toolbits into the local asset store."""
from ...toolbit.serializers import all_serializers as toolbit_serializers
from .util import get_serializer_from_extension
imported_count = 0
failed_imports = []
for dependency_uri, toolbit_file in external_toolbits:
try:
# Find appropriate serializer for the file
@@ -196,33 +197,39 @@ class AssetOpenDialog(QFileDialog):
serializer_class = get_serializer_from_extension(
toolbit_serializers, file_extension, for_import=True
)
if not serializer_class:
failed_imports.append(f"{dependency_uri.asset_id}: No serializer for {file_extension}")
failed_imports.append(
f"{dependency_uri.asset_id}: No serializer for {file_extension}"
)
continue
# Load and deserialize the toolbit
raw_toolbit_data = toolbit_file.read_bytes()
toolbit = serializer_class.deep_deserialize(raw_toolbit_data)
# Ensure the toolbit ID matches what the library expects
if toolbit.id != dependency_uri.asset_id:
toolbit.id = dependency_uri.asset_id
# Import the toolbit into local store
imported_count += 1
except Exception as e:
failed_imports.append(f"{dependency_uri.asset_id}: {str(e)}")
# Show results to user
if imported_count > 0:
message = f"Successfully imported {imported_count} toolbit(s) into your local store."
if failed_imports:
message += f"\n\nFailed to import {len(failed_imports)} toolbit(s):\n" + "\n".join(failed_imports)
message += f"\n\nFailed to import {len(failed_imports)} toolbit(s):\n" + "\n".join(
failed_imports
)
QMessageBox.information(self, "Import Results", message)
elif failed_imports:
message = f"Failed to import all {len(failed_imports)} toolbit(s):\n" + "\n".join(failed_imports)
message = f"Failed to import all {len(failed_imports)} toolbit(s):\n" + "\n".join(
failed_imports
)
QMessageBox.warning(self, "Import Failed", message)

View File

@@ -129,4 +129,4 @@ class AssetPreferencesPage:
asset_path = pref.GetString(Path.Preferences.ToolPath, "")
if not asset_path:
asset_path = str(Path.Preferences.getDefaultAssetPath())
self.asset_path_edit.setText(asset_path)
self.asset_path_edit.setText(asset_path)

View File

@@ -33,6 +33,7 @@ if False:
else:
Path.Log.setLevel(Path.Log.Level.INFO, Path.Log.thisModule())
def ensure_library_assets_initialized(asset_manager: AssetManager, store_name: str = "local"):
"""
Ensures the given store is initialized with built-in library

View File

@@ -72,7 +72,7 @@ class FCTLSerializer(AssetSerializer):
# for the asset being deserialized. We should use this ID for the library
# instance, overriding any 'id' that might be in the data_dict (which
# is from an older version of the format).
# For the label, prefer data_dict["label"], then "name", then fallback to using the id as filename
# The id parameter often contains the filename stem when importing from files
label = data_dict.get("label") or data_dict.get("name") or id or "Unnamed Library"
@@ -225,12 +225,12 @@ class FCTLSerializer(AssetSerializer):
Path.Log.info(
f"FCTL EXTERNAL: Toolbit '{dep_uri.asset_id}' not in stores, trying external file: {e}"
)
# Look for toolbit files in parallel Bit directory
library_dir = file_path.parent # e.g., /path/to/Library/
tools_dir = library_dir.parent # e.g., /path/to/
bit_dir = tools_dir / "Bit" # e.g., /path/to/Bit/
bit_dir = tools_dir / "Bit" # e.g., /path/to/Bit/
toolbit_loaded = False
if bit_dir.exists():
possible_extensions = [".fctb", ".json", ".yaml", ".yml"]
@@ -240,6 +240,7 @@ class FCTLSerializer(AssetSerializer):
try:
# Find appropriate serializer for the file
from ...assets.ui.util import get_serializer_from_extension
serializer_class = get_serializer_from_extension(
toolbit_serializers, ext, for_import=True
)
@@ -258,7 +259,7 @@ class FCTLSerializer(AssetSerializer):
f"FCTL EXTERNAL: Failed to load toolbit from {toolbit_file}: {load_error}"
)
continue
if not toolbit_loaded:
Path.Log.warning(
f"FCTL EXTERNAL: Could not load toolbit '{dep_uri.asset_id}' from external files"

View File

@@ -44,44 +44,44 @@ if FreeCAD.GuiUp:
from PySide.QtWidgets import QApplication, QMessageBox
from PySide.QtCore import Qt
class CAMAssetMigrator:
"""
Handles migration of CAM assets during FreeCAD version upgrades.
This class provides functionality to:
- Check if migration is needed for custom CAM asset locations
- Offer migration to users through a dialog
- Perform the actual asset migration with versioned directories
"""
def __init__(self):
self.pref_group_path = "User parameter:BaseApp/Preferences/Mod/CAM/Migration"
def check_migration_needed(self):
"""
Check if CAM asset migration is needed for version upgrade.
This method determines if the current CAM assets are stored in a custom
location outside the default user data directory and if migration has
not been offered for the current FreeCAD version.
Returns:
bool: True if migration should be offered, False otherwise
"""
Path.Log.info("Starting CAM asset migration check")
try:
# Get current directories
user_app_data_dir = FreeCAD.getUserAppDataDir()
user_app_data_path = pathlib.Path(user_app_data_dir)
Path.Log.debug(f"User app data directory: {user_app_data_dir}")
# Get the current CAM asset path (may be naked or versioned)
current_asset_path = Path.Preferences.getAssetPath()
current_asset_pathlib = pathlib.Path(current_asset_path)
Path.Log.debug(f"Current CAM asset path: {current_asset_path}")
# Only migrate if CamAssets is outside the standard user data directory
if current_asset_pathlib.is_relative_to(user_app_data_path):
Path.Log.debug("CamAssets is in default location, no custom migration needed")
@@ -95,7 +95,9 @@ class CAMAssetMigrator:
# Determine the base path (naked path without version)
if FreeCAD.ApplicationDirectories.isVersionedPath(str(current_asset_path)):
# Check if we're already using the current version
if FreeCAD.ApplicationDirectories.usingCurrentVersionConfig(str(current_asset_path)):
if FreeCAD.ApplicationDirectories.usingCurrentVersionConfig(
str(current_asset_path)
):
Path.Log.debug("Already using current version, no migration needed")
return
@@ -107,13 +109,14 @@ class CAMAssetMigrator:
except Exception as e:
Path.Log.error(f"Error checking CAM asset migration: {e}")
import traceback
Path.Log.debug(f"Full traceback: {traceback.format_exc()}")
return False
def _offer_migration_to_user(self):
"""
Present migration dialog to user.
Returns:
bool: True if user accepted migration, False otherwise
"""
@@ -121,16 +124,16 @@ class CAMAssetMigrator:
major = int(FreeCAD.ConfigGet("BuildVersionMajor"))
minor = int(FreeCAD.ConfigGet("BuildVersionMinor"))
current_version = FreeCAD.ApplicationDirectories.versionStringForPath(major, minor)
# Get current asset path for display
current_asset_path = Path.Preferences.getAssetPath()
Path.Log.debug(f"Offering migration to user for version {current_version}")
if not FreeCAD.GuiUp:
Path.Log.debug("GUI not available, skipping migration offer")
return False
msg = (
f"FreeCAD has been upgraded to version {current_version}.\n\n"
f"Your CAM assets are stored in a custom location:\n{current_asset_path}\n\n"
@@ -138,69 +141,64 @@ class CAMAssetMigrator:
"to preserve them during future upgrades?\n\n"
"This will copy your assets to a new directory."
)
Path.Log.debug("Showing migration dialog to user")
reply = QMessageBox.question(
None,
"CAM Asset Migration",
msg,
QMessageBox.Yes | QMessageBox.No,
QMessageBox.Yes
None, "CAM Asset Migration", msg, QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes
)
# Record that we offered migration for this version
pref_group = FreeCAD.ParamGet(self.pref_group_path)
offered_versions = pref_group.GetString("OfferedToMigrateCAMAssets", "")
known_versions = set(offered_versions.split(',')) if offered_versions else set()
known_versions = set(offered_versions.split(",")) if offered_versions else set()
known_versions.add(current_version)
pref_group.SetString("OfferedToMigrateCAMAssets", ",".join(known_versions))
Path.Log.debug(f"Updated offered versions: {known_versions}")
if reply == QMessageBox.Yes:
Path.Log.info("User accepted migration, starting asset migration")
return True
else:
Path.Log.info("User declined migration")
return False
def _migrate_assets(self, source_path):
"""
Perform actual directory copying and preference updates.
Args:
source_path: Current CAM asset directory path
"""
Path.Log.info(f"Starting asset migration from {source_path}")
try:
FreeCAD.ApplicationDirectories.migrateAllPaths([source_path])
Path.Log.info("Migration complete - preferences will be handled automatically by the system")
Path.Log.info(
"Migration complete - preferences will be handled automatically by the system"
)
if FreeCAD.GuiUp:
QMessageBox.information(
None,
"Migration Complete",
f"CAM assets have been migrated from:\n{source_path}\n\n"
"The system will automatically handle preference updates."
"The system will automatically handle preference updates.",
)
except Exception as e:
error_msg = f"Failed to migrate CAM assets: {e}"
Path.Log.error(error_msg)
import traceback
Path.Log.debug(f"Migration error traceback: {traceback.format_exc()}")
if FreeCAD.GuiUp:
QMessageBox.critical(
None,
"Migration Failed",
error_msg
)
QMessageBox.critical(None, "Migration Failed", error_msg)
def has_migration_been_offered(self):
"""
Check if migration has been offered for current version.
Returns:
bool: True if migration was offered for this version
"""
@@ -211,5 +209,5 @@ class CAMAssetMigrator:
current_version_string = FreeCAD.ApplicationDirectories.versionStringForPath(major, minor)
offered_versions = pref_group.GetString("OfferedToMigrateCAMAssets", "")
known_versions = set(offered_versions.split(',')) if offered_versions else set()
return current_version_string in known_versions
known_versions = set(offered_versions.split(",")) if offered_versions else set()
return current_version_string in known_versions

View File

@@ -277,8 +277,8 @@ class ToolBitBrowserWidget(QtGui.QWidget):
self,
FreeCAD.Qt.translate("CAM", "Missing Toolbit"),
FreeCAD.Qt.translate(
"CAM",
"This toolbit is missing from your local store. It may be a placeholder for a toolbit that was not found during library import."
"CAM",
"This toolbit is missing from your local store. It may be a placeholder for a toolbit that was not found during library import.",
),
)
except Exception as e: