flexicon.sync package¶
Submodules¶
flexicon.sync.conflict_resolvers module¶
Conflict Resolvers - Strategies for resolving sync conflicts
This module provides conflict resolution policies for sync operations.
Author: FlexTools Development Team Date: 2025-11-26
- class flexicon.sync.conflict_resolvers.ConflictResolver[source]¶
Bases:
ABCBase class for conflict resolution strategies.
When an object exists in both source and target but differs, a conflict resolver determines which version to keep.
Custom resolvers can be implemented by subclassing and implementing resolve().
- abstract resolve(source_obj: Any, target_obj: Any, source_project: Any, target_project: Any) Any[source]¶
Resolve conflict between source and target objects.
- Parameters:
source_obj – Source version of object
target_obj – Target version of object
source_project – Source FLExProject instance
target_project – Target FLExProject instance
- Returns:
The object to keep (either source_obj or target_obj)
- class flexicon.sync.conflict_resolvers.SourceWinsResolver[source]¶
Bases:
ConflictResolverResolve conflicts by always choosing the source version.
This is the most common strategy for one-way sync operations where the source is considered authoritative.
Use this when: - Source is the “master” or “stable” project - Syncing updates from development to production - You want to overwrite target with source data
Example
>>> resolver = SourceWinsResolver() >>> sync.sync( ... object_type="Allomorph", ... conflict_resolver=resolver ... )
- resolve(source_obj: Any, target_obj: Any, source_project: Any, target_project: Any) Any[source]¶
Always choose source version.
- Parameters:
source_obj – Source object (will be chosen)
target_obj – Target object (will be overwritten)
source_project – Source project
target_project – Target project
- Returns:
source_obj
- class flexicon.sync.conflict_resolvers.TargetWinsResolver[source]¶
Bases:
ConflictResolverResolve conflicts by always keeping the target version.
This strategy preserves the target project’s data when conflicts occur. Useful for selective merging or when target has priority.
Use this when: - Target contains more recent data - You want to preserve local changes - Syncing metadata but keeping target content
Example
>>> resolver = TargetWinsResolver() >>> sync.sync( ... object_type="Allomorph", ... conflict_resolver=resolver ... )
- resolve(source_obj: Any, target_obj: Any, source_project: Any, target_project: Any) Any[source]¶
Always keep target version.
- Parameters:
source_obj – Source object (will be ignored)
target_obj – Target object (will be kept)
source_project – Source project
target_project – Target project
- Returns:
target_obj
- class flexicon.sync.conflict_resolvers.NewestWinsResolver[source]¶
Bases:
ConflictResolverResolve conflicts by choosing the most recently modified version.
This strategy compares modification timestamps and chooses the newer version. Requires objects to have DateModified or similar timestamp properties.
Use this when: - Both source and target are actively maintained - You want automatic conflict resolution based on recency - Bidirectional sync scenarios
Example
>>> resolver = NewestWinsResolver() >>> sync.sync( ... object_type="LexEntry", ... conflict_resolver=resolver ... )
Note
Full implementation in Phase 2. Phase 1 placeholder.
- resolve(source_obj: Any, target_obj: Any, source_project: Any, target_project: Any) Any[source]¶
Choose version with most recent modification date.
- Parameters:
source_obj – Source object
target_obj – Target object
source_project – Source project
target_project – Target project
- Returns:
Newer object (source or target)
- class flexicon.sync.conflict_resolvers.ManualResolver[source]¶
Bases:
ConflictResolverResolve conflicts by prompting the user.
This strategy asks the user to manually choose which version to keep for each conflict. Useful for interactive sync operations.
Use this when: - Conflicts are rare and important - You want human review of changes - Data is critical and needs careful merging
Example
>>> resolver = ManualResolver() >>> sync.sync( ... object_type="LexEntry", ... conflict_resolver=resolver ... )
Note
Full implementation in Phase 2. Phase 1 placeholder.
- resolve(source_obj: Any, target_obj: Any, source_project: Any, target_project: Any) Any[source]¶
Prompt user to choose version.
- Parameters:
source_obj – Source object
target_obj – Target object
source_project – Source project
target_project – Target project
- Returns:
User-chosen object
- Raises:
NotImplementedError – Phase 1 - full implementation in Phase 2
- class flexicon.sync.conflict_resolvers.FieldMergeResolver(source_fields: List[str], target_fields: List[str])[source]¶
Bases:
ConflictResolverResolve conflicts by merging specific fields.
This strategy allows fine-grained control, merging some fields from source and keeping others from target.
Example
>>> # Keep target's gloss, but update form from source >>> resolver = FieldMergeResolver( ... source_fields=["form", "morph_type"], ... target_fields=["gloss", "definition"] ... )
Note
Full implementation in Phase 4. Phase 1 placeholder.
flexicon.sync.dependency_graph module¶
Dependency Graph - Phase 3.1
Models object dependencies as a directed acyclic graph (DAG) and provides topological sorting for correct import order.
Author: FlexTools Development Team Date: 2025-11-27
- class flexicon.sync.dependency_graph.DependencyType(value, names=<not given>, *values, module=None, qualname=None, type=None, start=1, boundary=None)[source]¶
Bases:
EnumTypes of dependencies between objects.
- OWNERSHIP = 'ownership'¶
- REFERENCE = 'reference'¶
- CROSS_REFERENCE = 'cross_ref'¶
- class flexicon.sync.dependency_graph.DependencyNode(guid: str, object_type: str, obj: ~typing.Any = None, dependencies: ~typing.Set[str] = <factory>, dependents: ~typing.Set[str] = <factory>, dependency_types: ~typing.Dict[str, ~flexicon.sync.dependency_graph.DependencyType] = <factory>, visited: bool = False, in_progress: bool = False)[source]¶
Bases:
objectNode in dependency graph representing a FLEx object.
- guid: str¶
- object_type: str¶
- obj: Any = None¶
- dependencies: Set[str]¶
- dependents: Set[str]¶
- dependency_types: Dict[str, DependencyType]¶
- visited: bool = False¶
- in_progress: bool = False¶
- class flexicon.sync.dependency_graph.DependencyGraph[source]¶
Bases:
objectDirected acyclic graph (DAG) of object dependencies.
Manages dependencies between FLEx objects and provides topological sorting to determine correct import order.
- add_object(guid: str, object_type: str, obj: Any = None)[source]¶
Add object to graph.
- Parameters:
guid – Object GUID
object_type – Type of object (e.g., “LexEntry”, “LexSense”)
obj – Actual FLEx object (optional)
- add_dependency(from_guid: str, to_guid: str, dep_type: DependencyType = DependencyType.REFERENCE)[source]¶
Add dependency: from_guid depends on to_guid.
This means to_guid must be imported before from_guid.
- Parameters:
from_guid – Object that depends on another
to_guid – Object that is depended upon
dep_type – Type of dependency
- remove_dependency(from_guid: str, to_guid: str)[source]¶
Remove dependency between objects.
Used for breaking cycles or handling optional dependencies.
- Parameters:
from_guid – Object that depends on another
to_guid – Object that is depended upon
- get_import_order() List[Tuple[str, str]][source]¶
Get topologically sorted import order.
Returns list of (guid, object_type) tuples in order they should be imported (dependencies first).
- Returns:
List of (guid, object_type) tuples
- Raises:
CircularDependencyError – If circular dependency detected
- detect_cycles() List[List[str]][source]¶
Detect circular dependencies using depth-first search.
- Returns:
List of cycles found, each cycle is a list of GUIDs
- get_roots() List[str][source]¶
Get root nodes (objects with no dependencies).
- Returns:
List of GUIDs with no dependencies
- get_leaves() List[str][source]¶
Get leaf nodes (objects that nothing depends on).
- Returns:
List of GUIDs with no dependents
- get_dependencies(guid: str, recursive: bool = False) List[str][source]¶
Get all dependencies for an object.
- Parameters:
guid – Object GUID
recursive – If True, get transitive dependencies
- Returns:
List of GUIDs this object depends on
- get_dependents(guid: str, recursive: bool = False) List[str][source]¶
Get all objects that depend on this object.
- Parameters:
guid – Object GUID
recursive – If True, get transitive dependents
- Returns:
List of GUIDs that depend on this object
- get_subgraph(guids: List[str]) DependencyGraph[source]¶
Extract subgraph containing only specified objects and their dependencies.
- Parameters:
guids – List of GUIDs to include
- Returns:
New DependencyGraph with only specified objects
flexicon.sync.dependency_resolver module¶
Dependency Resolver - Phase 3.2
Discovers and resolves object dependencies by analyzing FLEx object relationships.
Author: FlexTools Development Team Date: 2025-11-27
- class flexicon.sync.dependency_resolver.DependencyConfig(include_owned: bool = True, resolve_references: bool = True, include_referring: bool = False, max_owned_depth: int = 10, max_reference_depth: int = 2, owned_types: List[str] | None = None, reference_types: List[str] | None = None, skip_existing: bool = True, create_stub_parents: bool = False, validate_all: bool = True, allow_cycles: bool = False)[source]¶
Bases:
objectConfiguration for dependency resolution.
- include_owned: bool = True¶
- resolve_references: bool = True¶
- include_referring: bool = False¶
- max_owned_depth: int = 10¶
- max_reference_depth: int = 2¶
- owned_types: List[str] | None = None¶
- reference_types: List[str] | None = None¶
- skip_existing: bool = True¶
- create_stub_parents: bool = False¶
- validate_all: bool = True¶
- allow_cycles: bool = False¶
- class flexicon.sync.dependency_resolver.DependencyResolver(source_project: Any, target_project: Any)[source]¶
Bases:
objectAnalyzes FLEx objects and discovers all dependencies.
Builds dependency graph by examining object ownership, references, and cross-references.
- resolve_dependencies(obj: Any, object_type: str, config: DependencyConfig | None = None) DependencyGraph[source]¶
Build dependency graph for object and its dependencies.
- Parameters:
obj – FLEx object to analyze
object_type – Type of object
config – Dependency resolution configuration
- Returns:
DependencyGraph with all dependencies
- get_owned_objects(obj: Any, object_type: str) List[Tuple[Any, str]][source]¶
Get all objects owned by this object.
- Parameters:
obj – FLEx object
object_type – Type of object
- Returns:
List of (owned_object, object_type) tuples
flexicon.sync.diff module¶
DiffEngine - Object comparison and diff generation
This module provides diff functionality for comparing FLEx objects between projects.
Author: FlexTools Development Team Date: 2025-11-26
- class flexicon.sync.diff.ChangeType(value, names=<not given>, *values, module=None, qualname=None, type=None, start=1, boundary=None)[source]¶
Bases:
EnumType of change detected
- NEW = 'new'¶
- MODIFIED = 'modified'¶
- DELETED = 'deleted'¶
- CONFLICT = 'conflict'¶
- UNCHANGED = 'unchanged'¶
- class flexicon.sync.diff.Change(change_type: ~flexicon.sync.diff.ChangeType, source_guid: str | None, target_guid: str | None, object_type: str, description: str, details: ~typing.Dict[str, ~typing.Any] = <factory>)[source]¶
Bases:
objectRepresents a single detected change.
- Variables:
change_type (flexicon.sync.diff.ChangeType) – Type of change
source_guid (str | None) – GUID of source object (None if deleted)
target_guid (str | None) – GUID of target object (None if new)
object_type (str) – Type of object
description (str) – Human-readable description
details (Dict[str, Any]) – Additional details about the change
- change_type: ChangeType¶
- source_guid: str | None¶
- target_guid: str | None¶
- object_type: str¶
- description: str¶
- details: Dict[str, Any]¶
- property guid: str¶
Primary GUID for this change.
- class flexicon.sync.diff.DiffResult(object_type: str)[source]¶
Bases:
objectResults from a diff operation.
Contains all changes detected, organized by type, with methods for analysis and reporting.
- Usage:
>>> diff = sync.compare(object_type="Allomorph") >>> >>> print(f"Total changes: {diff.total}") >>> print(f"New: {diff.num_new}") >>> print(f"Modified: {diff.num_modified}") >>> >>> for change in diff.new_changes: ... print(f" {change.description}") >>> >>> print(diff.summary())
- property num_new: int¶
Count of NEW changes.
- property num_modified: int¶
Count of MODIFIED changes.
- property num_deleted: int¶
Count of DELETED changes.
- property num_conflicts: int¶
Count of CONFLICT changes.
- property num_unchanged: int¶
Count of UNCHANGED changes.
- property total: int¶
Total number of changes (excluding unchanged).
- property has_changes: bool¶
Whether any changes were detected.
- property has_conflicts: bool¶
Whether any conflicts were detected.
- summary() str[source]¶
Generate a text summary of changes.
- Returns:
Human-readable summary string
Example
>>> print(diff.summary()) DIFF SUMMARY: Allomorphs ---------------------------------------- 15 NEW (in source, not in target) 3 MODIFIED (different in source) 2 DELETED (in target, not in source) 0 CONFLICTS
- class flexicon.sync.diff.DiffEngine[source]¶
Bases:
objectEngine for comparing FLEx objects between projects.
The DiffEngine compares objects using a match strategy and detects all changes (new, modified, deleted, conflicts).
- Usage:
>>> from flexlibs2.sync import DiffEngine, GuidMatchStrategy >>> >>> engine = DiffEngine() >>> result = engine.compare( ... source_objects=source_project.Allomorph, ... target_objects=target_project.Allomorph, ... source_project=source_project, ... target_project=target_project, ... match_strategy=GuidMatchStrategy() ... )
- compare(source_objects: Any, target_objects: Any, source_project: Any, target_project: Any, match_strategy: MatchStrategy, filter_fn: Callable | None = None, progress_callback: Callable[[str], None] | None = None) DiffResult[source]¶
Compare objects between source and target.
- Parameters:
source_objects – Source operations class instance
target_objects – Target operations class instance
source_project – Source FLExProject
target_project – Target FLExProject
match_strategy – Strategy for matching objects
filter_fn – Optional filter function
progress_callback – Optional progress callback
- Returns:
DiffResult with all changes
flexicon.sync.engine module¶
SyncEngine - Core orchestrator for multi-database synchronization
This module provides the main SyncEngine class that coordinates all sync operations.
Author: FlexTools Development Team Date: 2025-11-26
- class flexicon.sync.engine.SyncMode(value, names=<not given>, *values, module=None, qualname=None, type=None, start=1, boundary=None)[source]¶
Bases:
EnumSync operation mode
- READONLY = 'readonly'¶
- WRITE = 'write'¶
- class flexicon.sync.engine.SyncEngine(source_project: Any, target_project: Any, mode: SyncMode | None = None)[source]¶
Bases:
objectMain orchestrator for database synchronization operations.
The SyncEngine coordinates comparison and synchronization of FLEx objects between two projects. It respects the writeEnabled flag from FlexTools to determine whether to show diffs (readonly) or execute syncs (write).
- Usage:
>>> from flexlibs2.sync import SyncEngine, GuidMatchStrategy >>> >>> # Create engine >>> sync = SyncEngine( ... source_project=source_project, ... target_project=target_project ... ) >>> >>> # Readonly mode - show diff only >>> diff = sync.compare( ... object_type="Allomorph", ... match_strategy=GuidMatchStrategy() ... ) >>> print(diff.summary()) >>> >>> # Write mode - execute sync >>> result = sync.sync( ... object_type="Allomorph", ... match_strategy=GuidMatchStrategy(), ... conflict_resolver="source_wins" ... )
- register_strategy(name: str, strategy: MatchStrategy) None[source]¶
Register a custom match strategy.
- Parameters:
name – Strategy name for later reference
strategy – MatchStrategy instance
Example
>>> class CustomMatcher(MatchStrategy): ... def match(self, source_obj, target_candidates, source_project, target_project): ... # Custom matching logic ... pass >>> >>> sync.register_strategy("custom", CustomMatcher()) >>> diff = sync.compare(object_type="Entry", match_strategy="custom")
- register_resolver(name: str, resolver: ConflictResolver) None[source]¶
Register a custom conflict resolver.
- Parameters:
name – Resolver name for later reference
resolver – ConflictResolver instance
- compare(object_type: str, match_strategy: str | MatchStrategy | None = None, filter_fn: Callable | None = None, progress_callback: Callable[[str], None] | None = None) DiffResult[source]¶
Compare objects between source and target projects (readonly).
This method performs a diff-only operation, showing what would change if a sync were executed. It never modifies the target database.
- Parameters:
object_type – Type of objects to compare (e.g., “Allomorph”, “LexEntry”)
match_strategy – Strategy for matching objects (default: GUID-based)
filter_fn – Optional filter function to include only specific objects
progress_callback – Optional callback for progress updates
- Returns:
DiffResult containing all changes detected
Example
>>> diff = sync.compare( ... object_type="Allomorph", ... match_strategy=GuidMatchStrategy() ... ) >>> >>> print(f"New: {diff.num_new}") >>> print(f"Modified: {diff.num_modified}") >>> print(f"Deleted: {diff.num_deleted}")
- sync(object_type: str, match_strategy: str | MatchStrategy | None = None, conflict_resolver: str | ConflictResolver = 'source_wins', include_dependencies: bool = False, filter_fn: Callable | None = None, progress_callback: Callable[[str], None] | None = None, dry_run: bool = False) SyncResult[source]¶
Synchronize objects from source to target project (write mode).
This method executes sync operations, creating/updating/deleting objects in the target project. Requires target project opened with writeEnabled=True.
- Parameters:
object_type – Type of objects to sync
match_strategy – Strategy for matching objects (default: GUID-based)
conflict_resolver – How to resolve conflicts (default: “source_wins”)
include_dependencies – Auto-create missing dependencies (default: False)
filter_fn – Optional filter function
progress_callback – Optional callback for progress updates
dry_run – If True, show what would happen without making changes
- Returns:
SyncResult with statistics and details
- Raises:
RuntimeError – If in readonly mode or target not writable
Example
>>> result = sync.sync( ... object_type="Allomorph", ... match_strategy=GuidMatchStrategy(), ... conflict_resolver="source_wins" ... ) >>> >>> print(f"Created: {result.num_created}") >>> print(f"Updated: {result.num_updated}") >>> print(f"Deleted: {result.num_deleted}")
- compare_possibility_list(list_type: str, match_strategy: str | MatchStrategy | None = None) DiffResult[source]¶
Compare possibility lists (POS, MorphTypes, etc.) between projects.
- Parameters:
list_type – Type of possibility list (e.g., “PartsOfSpeech”)
match_strategy – Strategy for matching (default: GUID-based)
- Returns:
DiffResult for the possibility list
Note
This is a specialized comparison for hierarchical possibility lists. Full implementation in Phase 4.
- validate_dependencies(object_type: str, auto_create_missing: bool = False) DependencyValidation[source]¶
Validate that all dependencies exist in target project.
- Parameters:
object_type – Type of objects to validate
auto_create_missing – If True, auto-create missing dependencies
- Returns:
DependencyValidation result
Note
Full implementation in Phase 3 (Dependency Safety).
- class flexicon.sync.engine.SyncResult(object_type: str)[source]¶
Bases:
objectResults from a sync operation.
Tracks all changes made during a sync, including creates, updates, deletes, and any errors encountered.
- Usage:
>>> result = sync.sync(object_type="Allomorph") >>> print(f"Created: {result.num_created}") >>> print(f"Updated: {result.num_updated}") >>> result.export_log("sync_log.txt")
- property total: int¶
Total number of operations performed.
- property success: bool¶
Whether sync completed without errors.
- add_change(change: SyncChange) None[source]¶
Record a change made during sync.
flexicon.sync.export module¶
Report Exporter - Generate diff reports in various formats
This module provides export functionality for diff results.
Author: FlexTools Development Team Date: 2025-11-26
- class flexicon.sync.export.ReportExporter[source]¶
Bases:
objectExport diff reports in various formats.
Phase 1: Console and Markdown Phase 4: HTML and CSV
- Usage:
>>> exporter = ReportExporter() >>> exporter.export_console(diff, verbose=True) >>> exporter.export_markdown(diff, filename="report.md")
- export_console(diff: DiffResult, verbose: bool = False) str[source]¶
Generate console-formatted report.
- Parameters:
diff – DiffResult to export
verbose – Include unchanged items
- Returns:
Formatted console text
- export_markdown(diff: DiffResult, filename: str | None = None, verbose: bool = False) str[source]¶
Generate markdown-formatted report.
- Parameters:
diff – DiffResult to export
filename – Optional file to write to
verbose – Include unchanged items
- Returns:
Formatted markdown text
- export_html(diff: DiffResult, filename: str, verbose: bool = False) None[source]¶
Generate HTML report with interactive features.
Note
Full implementation in Phase 4.
- Parameters:
diff – DiffResult to export
filename – Output HTML file
verbose – Include unchanged items
- Raises:
NotImplementedError – Phase 1 - implemented in Phase 4
- export_csv(diff: DiffResult, filename: str, verbose: bool = False) None[source]¶
Generate CSV report for spreadsheet analysis.
Note
Full implementation in Phase 4.
- Parameters:
diff – DiffResult to export
filename – Output CSV file
verbose – Include unchanged items
- Raises:
NotImplementedError – Phase 1 - implemented in Phase 4
flexicon.sync.hierarchical_importer module¶
Hierarchical Importer - Phase 3.3
Import FLEx objects with full dependency resolution and hierarchical cascade.
Author: FlexTools Development Team Date: 2025-11-27
- class flexicon.sync.hierarchical_importer.HierarchicalImporter(source_project: Any, target_project: Any)[source]¶
Bases:
objectImport FLEx objects with dependency resolution.
Automatically discovers and imports all required dependencies, including owned objects (senses, examples) and referenced objects (POS, semantic domains).
- import_with_dependencies(object_type: str, guids: List[str], config: DependencyConfig | None = None, validate_references: bool = True, progress_callback: Callable[[str], None] | None = None, dry_run: bool = False) SyncResult[source]¶
Import objects with full dependency trees.
Discovers all dependencies (owned objects, referenced objects) and imports them in correct order.
- Parameters:
object_type – Type of root objects to import
guids – List of GUIDs to import
config – Dependency resolution configuration
validate_references – Validate all objects before import
progress_callback – Optional callback for progress updates
dry_run – If True, preview without actually importing
- Returns:
SyncResult with import details
- Raises:
CircularDependencyError – If circular dependency detected
ValidationError – If validation fails with critical issues
Import object and all objects that refer to it.
Useful for importing a semantic domain with all entries that use it, or a POS with all senses that reference it.
- Parameters:
object_type – Type of object to import
guid – GUID of object
include_referring_objects – List of object types to import if they refer to this
validate_references – Validate all objects
progress_callback – Optional callback for progress updates
dry_run – If True, preview without importing
- Returns:
SyncResult with import details
flexicon.sync.match_strategies module¶
Match Strategies - Pluggable object matching strategies
This module provides strategies for matching objects between source and target projects.
Author: FlexTools Development Team Date: 2025-11-26
- class flexicon.sync.match_strategies.MatchStrategy[source]¶
Bases:
ABCBase class for object matching strategies.
Match strategies determine how to match objects between source and target projects. Common strategies include GUID-based (for backups/forks) and field-based (for cross-project merging).
Custom strategies can be implemented by sub classing and implementing match().
- abstract match(source_obj: Any, target_candidates: List[Any], source_project: Any, target_project: Any) Any | None[source]¶
Find matching target object for source object.
- Parameters:
source_obj – Source object to match
target_candidates – List of all target objects to search
source_project – Source FLExProject instance
target_project – Target FLExProject instance
- Returns:
Matching target object, or None if no match found
- class flexicon.sync.match_strategies.GuidMatchStrategy[source]¶
Bases:
MatchStrategyMatch objects by GUID (Globally Unique Identifier).
This is the safest and most reliable matching strategy for projects that share a common history (backups, forks, branches). GUIDs are persistent across projects and never change.
Use this strategy when: - Syncing between backup and main project - Syncing between test branch and stable branch - Source and target share common ancestry
Example
>>> strategy = GuidMatchStrategy() >>> match = strategy.match(source_obj, target_objects, source_proj, target_proj)
- match(source_obj: Any, target_candidates: List[Any], source_project: Any, target_project: Any) Any | None[source]¶
Match by GUID.
- Parameters:
source_obj – Source object with .Guid property
target_candidates – Target objects to search
source_project – Source project (unused)
target_project – Target project (unused)
- Returns:
Target object with matching GUID, or None
- class flexicon.sync.match_strategies.FieldMatchStrategy(key_fields: List[str], get_field_fn: callable | None = None, case_sensitive: bool = True, writing_system: str | None = None)[source]¶
Bases:
MatchStrategyMatch objects by field values.
This strategy matches objects based on specified field values (e.g., headword, form, name). Useful for merging independent projects that don’t share GUIDs.
Use this strategy when: - Merging data from different projects - Source and target have no common history - Matching by semantic equivalence rather than identity
Example
>>> # Match allomorphs by form >>> strategy = FieldMatchStrategy( ... key_fields=["form"], ... get_field_fn=lambda obj, ops: ops.GetForm(obj) ... ) >>> >>> # Match entries by headword >>> strategy = FieldMatchStrategy( ... key_fields=["headword"], ... get_field_fn=lambda obj, ops: ops.GetHeadword(obj) ... )
- match(source_obj: Any, target_candidates: List[Any], source_project: Any, target_project: Any) Any | None[source]¶
Match by field values.
- Parameters:
source_obj – Source object
target_candidates – Target objects to search
source_project – Source project (for operations access)
target_project – Target project (for operations access)
- Returns:
First target object with matching field values, or None
- class flexicon.sync.match_strategies.HybridMatchStrategy(fallback_fields: List[str], case_sensitive: bool = True)[source]¶
Bases:
MatchStrategyHybrid matching: Try GUID first, fall back to field-based.
This strategy combines GUID and field-based matching. It first attempts to match by GUID (fastest, most reliable), and if that fails, falls back to field-based matching.
Use this strategy when: - Source and target may partially overlap - Some objects share GUIDs, others don’t - You want reliability of GUID with flexibility of field matching
Example
>>> strategy = HybridMatchStrategy( ... fallback_fields=["headword"], ... case_sensitive=False ... )
- match(source_obj: Any, target_candidates: List[Any], source_project: Any, target_project: Any) Any | None[source]¶
Try GUID match first, then field match.
- Parameters:
source_obj – Source object
target_candidates – Target objects
source_project – Source project
target_project – Target project
- Returns:
Matched target object, or None
flexicon.sync.merge_ops module¶
MergeOperations - Safe execution of sync operations
This module provides safe Create/Update/Delete operations for syncing FLEx objects between projects.
Author: FlexTools Development Team Date: 2025-11-26
- class flexicon.sync.merge_ops.MergeOperations(target_project: Any)[source]¶
Bases:
objectExecute sync operations safely.
This class handles the actual creation, updating, and deletion of FLEx objects during sync operations. All operations include validation and error handling.
- Usage:
>>> merger = MergeOperations(target_project) >>> merger.create_object( ... target_ops=target_project.Allomorph, ... source_obj=source_allomorph, ... source_ops=source_project.Allomorph, ... parent_obj=target_entry ... )
- create_object(target_ops: Any, source_obj: Any, source_ops: Any, parent_obj: Any | None = None, **create_kwargs) Any[source]¶
Create a new object in target project.
This method creates a new object by extracting properties from the source object and calling the appropriate Operations.Create() method.
- Parameters:
target_ops – Target operations instance (e.g., target_project.Allomorph)
source_obj – Source object to copy from
source_ops – Source operations instance
parent_obj – Optional parent object (for owned objects)
**create_kwargs – Additional arguments for Create() method
- Returns:
Newly created object in target
- Raises:
RuntimeError – If creation fails
AttributeError – If Create method not available
Example
>>> # Create allomorph in target >>> new_allomorph = merger.create_object( ... target_ops=target_project.Allomorph, ... source_obj=source_allomorph, ... source_ops=source_project.Allomorph, ... parent_obj=target_entry ... )
- update_object(target_obj: Any, source_obj: Any, source_ops: Any, target_ops: Any, fields: List[str] | None = None) bool[source]¶
Update an existing object in target project.
Copies properties from source object to target object. If fields is specified, only those fields are updated. Otherwise, all common properties are updated.
This method will attempt to use the new CompareTo() method if available on the operations class to efficiently detect differences. Falls back to traditional property-by-property copying if not available.
- Parameters:
target_obj – Target object to update
source_obj – Source object to copy from
source_ops – Source operations instance
target_ops – Target operations instance
fields – Optional list of specific fields to update
- Returns:
True if any changes were made
- Raises:
RuntimeError – If update fails
Example
>>> # Update allomorph form only >>> changed = merger.update_object( ... target_obj=target_allomorph, ... source_obj=source_allomorph, ... source_ops=source_project.Allomorph, ... target_ops=target_project.Allomorph, ... fields=["form"] ... )
- delete_object(target_ops: Any, target_obj: Any, validate_safe: bool = True) bool[source]¶
Delete an object from target project.
- Parameters:
target_ops – Target operations instance
target_obj – Object to delete
validate_safe – If True, perform safety checks before deleting
- Returns:
True if deleted successfully
- Raises:
RuntimeError – If deletion fails or is unsafe
Example
>>> # Delete allomorph >>> deleted = merger.delete_object( ... target_ops=target_project.Allomorph, ... target_obj=target_allomorph ... )
- copy_properties(source_obj: Any, target_obj: Any, source_ops: Any, target_ops: Any) bool[source]¶
Copy properties from source to target object.
This method first attempts to use the new GetSyncableProperties() method from the sync framework if available. This provides complete, accurate property coverage for all object types.
If GetSyncableProperties() is not available, falls back to traditional pattern matching of Get*/Set* methods for backwards compatibility.
- Parameters:
source_obj – Source object to copy from
target_obj – Target object to copy to
source_ops – Source operations instance
target_ops – Target operations instance
- Returns:
True if any properties were changed
Example
>>> # Copy all properties >>> changed = merger.copy_properties( ... source_allomorph, ... target_allomorph, ... source_project.Allomorph, ... target_project.Allomorph ... )
flexicon.sync.selective_import module¶
Selective Import Operations
One-way import of specific objects from source to target project. Matches the actual linguistic workflow: “import new allomorphs to stable project”.
Author: FlexTools Development Team Date: 2025-11-27
- class flexicon.sync.selective_import.SelectiveImport(source_project: Any, target_project: Any)[source]¶
Bases:
objectOne-way selective import from source to target project.
This matches the linguist’s actual workflow: 1. Copy project for testing 2. Make changes in test project 3. Import ONLY new/modified items back to stable project 4. NEVER overwrite stable project’s existing data
- Usage:
>>> importer = SelectiveImport(source_project, target_project) >>> result = importer.import_new_objects( ... object_type="Allomorph", ... created_after=backup_timestamp, ... validate_references=True ... ) >>> print(result.summary())
This is SAFER than bidirectional sync for linguistic data.
- import_new_objects(object_type: str, created_after: datetime | None = None, modified_after: datetime | None = None, validate_references: bool = True, include_owned: bool = False, progress_callback: Callable[[str], None] | None = None, dry_run: bool = False) SyncResult[source]¶
Import objects created/modified after specified date.
This is the PRIMARY method for linguistic workflows: - Imports NEW objects only (not in target) - Filters by creation/modification date - NEVER overwrites existing target data - One-way operation (source → target only)
- Parameters:
object_type – Type to import (e.g., “Allomorph”, “LexEntry”)
created_after – Only import objects created after this time
modified_after – Only import objects modified after this time
validate_references – Check for missing references before import
include_owned – Also import owned objects (WARNING: complex)
progress_callback – Optional progress updates
dry_run – Preview without making changes
- Returns:
SyncResult with import statistics
Example
>>> # User workflow: backup → test → import new >>> backup_time = datetime(2025, 11, 1) >>> # ... work in test project ... >>> result = importer.import_new_objects( ... object_type="Allomorph", ... created_after=backup_time, ... validate_references=True ... ) >>> print(f"Imported {result.num_created} new allomorphs")
- import_by_filter(object_type: str, filter_fn: Callable[[Any], bool], validate_references: bool = True, progress_callback: Callable[[str], None] | None = None, dry_run: bool = False) SyncResult[source]¶
Import objects matching custom filter function.
- Parameters:
object_type – Type to import
filter_fn – Function that returns True for objects to import
validate_references – Validate before import
progress_callback – Progress updates
dry_run – Preview mode
- Returns:
SyncResult with statistics
Example
>>> # Import only verified allomorphs >>> def is_verified(obj): ... return hasattr(obj, 'Status') and obj.Status == 'Verified' >>> result = importer.import_by_filter( ... object_type="Allomorph", ... filter_fn=is_verified ... )
flexicon.sync.validation module¶
Linguistic Validation Framework
Validates linguistic data integrity before sync operations. Prevents orphaned references, broken hierarchies, and data corruption.
Author: FlexTools Development Team Date: 2025-11-27
- class flexicon.sync.validation.ValidationSeverity(value, names=<not given>, *values, module=None, qualname=None, type=None, start=1, boundary=None)[source]¶
Bases:
EnumSeverity levels for validation issues.
- CRITICAL = 'critical'¶
- WARNING = 'warning'¶
- INFO = 'info'¶
- class flexicon.sync.validation.ValidationIssue(severity: ~flexicon.sync.validation.ValidationSeverity, category: str, object_type: str, object_guid: str, message: str, details: ~typing.Dict[str, ~typing.Any] = <factory>)[source]¶
Bases:
objectA single validation issue found during pre-sync checks.
- Variables:
severity (flexicon.sync.validation.ValidationSeverity) – How serious the issue is
category (str) – Type of issue (reference, hierarchy, etc.)
object_type (str) – Type of object with issue
object_guid (str) – GUID of problematic object
message (str) – Human-readable description
details (Dict[str, Any]) – Additional context
- severity: ValidationSeverity¶
- category: str¶
- object_type: str¶
- object_guid: str¶
- message: str¶
- details: Dict[str, Any]¶
- class flexicon.sync.validation.ValidationResult(issues: ~typing.List[~flexicon.sync.validation.ValidationIssue] = <factory>)[source]¶
Bases:
objectResults from validation checks.
Tracks all issues found and provides summary.
- issues: List[ValidationIssue]¶
- property has_critical: bool¶
Check if any critical issues found.
- property has_warnings: bool¶
Check if any warnings found.
- property num_critical: int¶
Count critical issues.
- property num_warnings: int¶
Count warnings.
- property num_info: int¶
Count info messages.
- add_issue(issue: ValidationIssue)[source]¶
Add a validation issue.
- class flexicon.sync.validation.LinguisticValidator(target_project: Any)[source]¶
Bases:
objectValidates linguistic data integrity before sync operations.
Checks for: - Missing reference targets (POS, semantic domains, etc.) - Orphaned owned objects (phonological environments, etc.) - Broken hierarchical relationships - Invalid linguistic constraints
- Usage:
>>> validator = LinguisticValidator(target_project) >>> result = validator.validate_before_create(source_obj, source_project) >>> if result.has_critical: ... raise ValidationError(result.summary())
- validate_before_create(source_obj: Any, source_project: Any, object_type: str) ValidationResult[source]¶
Validate object before creating in target.
- Parameters:
source_obj – Object to be created
source_project – Source project
object_type – Type of object
- Returns:
ValidationResult with any issues found
- exception flexicon.sync.validation.ValidationError(validation_result: ValidationResult)[source]¶
Bases:
ExceptionRaised when validation fails with critical issues.
Module contents¶
Multi-Database Sync/Merge Framework for flexlibs
This package provides tools for synchronizing and merging FLEx database objects between projects, with support for FlexTools integration, conflict resolution, and dependency management.
Author: FlexTools Development Team Date: 2025-11-26