pyflexicon 4.1.2__tar.gz → 4.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/CHANGELOG.md +59 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/PKG-INFO +1 -1
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/BaseOperations.py +80 -11
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Discourse/ConstChartOperations.py +2 -2
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/FLExProject.py +47 -56
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Grammar/MorphRuleOperations.py +9 -3
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Lexicon/AllomorphOperations.py +26 -40
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Lexicon/LexEntryOperations.py +23 -49
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Lexicon/LexReferenceOperations.py +6 -3
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Lexicon/LexSenseOperations.py +11 -24
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Lexicon/MSAOperations.py +26 -9
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Lexicon/VariantOperations.py +1 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Lists/TranslationTypeOperations.py +2 -1
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Notebook/DataNotebookOperations.py +1 -1
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Notebook/NoteOperations.py +1 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Notebook/PersonOperations.py +2 -2
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Reversal/ReversalIndexOperations.py +3 -2
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Scripture/ScrBookOperations.py +2 -2
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Scripture/ScrDraftOperations.py +2 -2
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Shared/FilterOperations.py +3 -2
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Shared/MediaOperations.py +77 -17
- pyflexicon-4.2.0/flexicon/code/Shared/morph_type_utils.py +177 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Shared/string_utils.py +81 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/System/AnnotationDefOperations.py +1 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/System/CheckOperations.py +3 -1
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/System/WritingSystemOperations.py +2 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/TextsWords/DiscourseOperations.py +1 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/TextsWords/ParagraphOperations.py +3 -2
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/TextsWords/SegmentOperations.py +52 -1
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/TextsWords/TextOperations.py +1 -1
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/TextsWords/WfiAnalysisOperations.py +120 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/TextsWords/WfiGlossOperations.py +75 -66
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/TextsWords/WordformOperations.py +6 -4
- pyflexicon-4.2.0/flexicon/code/_op_aliases.py +156 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/transaction.py +13 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/pyflexicon.egg-info/PKG-INFO +1 -1
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/pyflexicon.egg-info/SOURCES.txt +4 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/pyproject.toml +1 -1
- pyflexicon-4.2.0/tests/test_flexproject_aliases.py +171 -0
- pyflexicon-4.2.0/tests/test_wrap_enumerable_pattern.py +247 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/LICENSE.txt +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/MANIFEST.in +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/NOTICE +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/README.rst +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/__init__.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/__init__.pyi +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/BaseOperations.pyi +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Discourse/ConstChartCellTagOperations.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Discourse/ConstChartClauseMarkerOperations.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Discourse/ConstChartMarkerOperations.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Discourse/ConstChartMovedTextOperations.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Discourse/ConstChartRowOperations.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Discourse/ConstChartWordGroupOperations.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Discourse/__init__.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/FLExGlobals.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/FLExInit.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/FLExLCM.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/FLExProject.pyi +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Grammar/AllomorphOperations.pyi +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Grammar/EnvironmentOperations.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Grammar/EnvironmentOperations.pyi +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Grammar/GramCatOperations.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Grammar/GramCatOperations.pyi +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Grammar/InflectionFeatureOperations.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Grammar/InflectionFeatureOperations.pyi +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Grammar/MorphRuleOperations.pyi +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Grammar/NaturalClassOperations.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Grammar/NaturalClassOperations.pyi +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Grammar/POSOperations.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Grammar/POSOperations.pyi +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Grammar/PhonFeatureOperations.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Grammar/PhonemeOperations.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Grammar/PhonemeOperations.pyi +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Grammar/PhonologicalRuleOperations.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Grammar/PhonologicalRuleOperations.pyi +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Grammar/StratumOperations.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Grammar/__init__.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Grammar/adhoc_prohibition.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Grammar/affix_template.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Grammar/affix_template_collection.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Grammar/compound_rule.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Grammar/compound_rule_collection.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Grammar/phonological_rule.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Grammar/prohibition_collection.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Grammar/rule_collection.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Lexicon/EtymologyOperations.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Lexicon/EtymologyOperations.pyi +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Lexicon/ExampleOperations.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Lexicon/ExampleOperations.pyi +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Lexicon/LexEntryOperations.pyi +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Lexicon/LexReferenceOperations.pyi +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Lexicon/LexSenseOperations.pyi +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Lexicon/PronunciationOperations.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Lexicon/PronunciationOperations.pyi +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Lexicon/SemanticDomainOperations.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Lexicon/SemanticDomainOperations.pyi +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Lexicon/VariantOperations.pyi +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Lexicon/__init__.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Lexicon/allomorph.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Lexicon/allomorph_collection.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Lexicon/morphosyntax_analysis.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Lexicon/msa_collection.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Lists/AgentOperations.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Lists/AgentOperations.pyi +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Lists/ConfidenceOperations.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Lists/ConfidenceOperations.pyi +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Lists/LocalizedListsOperations.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Lists/OverlayOperations.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Lists/OverlayOperations.pyi +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Lists/PossibilityListOperations.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Lists/PossibilityListOperations.pyi +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Lists/PublicationOperations.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Lists/PublicationOperations.pyi +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Lists/TranslationTypeOperations.pyi +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Lists/__init__.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Lists/possibility_item_base.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Notebook/AnthropologyOperations.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Notebook/AnthropologyOperations.pyi +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Notebook/DataNotebookOperations.pyi +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Notebook/LocationOperations.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Notebook/LocationOperations.pyi +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Notebook/NoteOperations.pyi +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Notebook/PersonOperations.pyi +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Notebook/__init__.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Notebook/annotation.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Notebook/annotation_collection.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/PythonicWrapper.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Reversal/ReversalIndexEntryOperations.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Reversal/__init__.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Scripture/ScrAnnotationsOperations.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Scripture/ScrNoteOperations.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Scripture/ScrSectionOperations.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Scripture/ScrTxtParaOperations.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Scripture/__init__.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Shared/__init__.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Shared/catalog.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Shared/catalog_backed.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Shared/lcm_constants.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Shared/rule_patterns.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Shared/smart_collection.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/Shared/wrapper_base.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/System/AnnotationDefOperations.pyi +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/System/CheckOperations.pyi +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/System/CustomFieldOperations.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/System/CustomFieldOperations.pyi +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/System/ProjectSettingsOperations.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/System/ProjectSettingsOperations.pyi +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/System/WritingSystemOperations.pyi +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/System/__init__.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/System/context_collection.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/System/phonological_context.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/TextsWords/DiscourseOperations.pyi +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/TextsWords/FilterOperations.pyi +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/TextsWords/MediaOperations.pyi +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/TextsWords/ParagraphOperations.pyi +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/TextsWords/SegmentOperations.pyi +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/TextsWords/TextOperations.pyi +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/TextsWords/WfiAnalysisOperations.pyi +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/TextsWords/WfiGlossOperations.pyi +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/TextsWords/WfiMorphBundleOperations.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/TextsWords/WfiMorphBundleOperations.pyi +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/TextsWords/WordformOperations.pyi +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/TextsWords/__init__.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/__init__.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/__init__.pyi +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/_context_manager_base.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/exceptions.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/lcm_casting.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/code/undoable_operation.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/examples/demo_lexicon.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/examples/demo_openproject.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/examples/demo_pos_operations.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/examples/demo_writing_systems.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/py.typed +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/sync/__init__.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/sync/conflict_resolvers.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/sync/dependency_graph.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/sync/dependency_resolver.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/sync/diff.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/sync/engine.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/sync/export.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/sync/hierarchical_importer.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/sync/match_strategies.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/sync/merge_ops.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/sync/selective_import.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/sync/tests/__init__.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/sync/tests/test_base_operations.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/sync/tests/test_dependency_graph.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/sync/tests/test_diff_engine.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/sync/tests/test_duplicate_operations.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/sync/tests/test_match_strategies.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/sync/tests/test_merge_ops.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/sync/tests/test_selective_import.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/sync/tests/test_sync_engine.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/sync/tests/test_sync_result.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/sync/tests/test_validation.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/sync/validation.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/tests/test_CustomFields.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/tests/test_FLExInit.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexicon/tests/test_FLExProject.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/flexlibs2/__init__.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/pyflexicon.egg-info/dependency_links.txt +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/pyflexicon.egg-info/requires.txt +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/pyflexicon.egg-info/top_level.txt +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/setup.cfg +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/tests/test_affix_template_wrappers.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/tests/test_allomorph_wrappers.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/tests/test_annotation_wrappers.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/tests/test_catalog.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/tests/test_collections.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/tests/test_compound_rule_wrappers.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/tests/test_consolidation_coverage.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/tests/test_context_wrappers.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/tests/test_custom_field_create_refusal.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/tests/test_exception_handling.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/tests/test_fixtures.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/tests/test_flexproject_discoverability.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/tests/test_helpers.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/tests/test_homographs.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/tests/test_itsstring_fix.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/tests/test_lcm_api_real.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/tests/test_lcm_api_usage.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/tests/test_lcm_direct.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/tests/test_lcm_method_verification.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/tests/test_msa_wrappers.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/tests/test_normalize_match_key.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/tests/test_normalize_ws_handle.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/tests/test_operations_baseline.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/tests/test_pattern_g_owner_of_class.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/tests/test_pattern_writing_systems_enumeration.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/tests/test_phoneme_duplicate_fix.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/tests/test_phonological_rules_wrappers.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/tests/test_prohibition_wrappers.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/tests/test_rule_patterns.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/tests/test_segment_baseline_text.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/tests/test_sense_lookups.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/tests/test_validation_base.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/tests/test_wfianalysis_agent_import.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/tests/test_wrappers.py +0 -0
- {pyflexicon-4.1.2 → pyflexicon-4.2.0}/tests/test_write_enabled_fix.py +0 -0
|
@@ -15,6 +15,65 @@ _Nothing yet. Non-breaking fixes and breaking changes accumulate here until the
|
|
|
15
15
|
|
|
16
16
|
---
|
|
17
17
|
|
|
18
|
+
## [4.2.0]
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
- **`MsaFactoryOperations.CreateStem` / `CreateUnclassifiedAffix`** now
|
|
22
|
+
accept `pos=None` for a category-less stem or unclassified-affix MSA
|
|
23
|
+
(a valid, blank-category-cell FLEx state), mirroring the existing
|
|
24
|
+
`CreateDerivAff` `to_pos=None` precedent. Unblocks cross-project
|
|
25
|
+
transfer of category-less MSAs (9c04ab8).
|
|
26
|
+
|
|
27
|
+
### Fixed
|
|
28
|
+
- **`MsaFactoryOperations.CreateInflAff`** accepts `pos=None` for a
|
|
29
|
+
category-less inflectional affix, matching `CreateStem` /
|
|
30
|
+
`CreateUnclassifiedAffix` (6f316b3).
|
|
31
|
+
- **Morph-type classification** unified into a single shared module
|
|
32
|
+
(`Shared/morph_type_utils.py`); `LexEntryOperations`,
|
|
33
|
+
`AllomorphOperations`, and `LexSenseOperations` no longer each carry
|
|
34
|
+
independently drifting stem/affix GUID sets. Fixes a silent logical
|
|
35
|
+
inversion in `LexSenseOperations.__EntryHasAffixMorphType`'s GUID set.
|
|
36
|
+
`MorphRuleOperations.Duplicate` now defaults `deep=True`, matching the
|
|
37
|
+
LexEntry/Text family (#203, #213, #214).
|
|
38
|
+
- **`wrap_enumerable`** now catches bare Python iterators/generators (not
|
|
39
|
+
just raw C# `IEnumerable`), so `GetAll()`/`GetAnalyses()`-style methods
|
|
40
|
+
across ~20 Operations classes reliably return a subscriptable,
|
|
41
|
+
`len()`-able collection instead of raising `TypeError` on `entries[0]`
|
|
42
|
+
or `len(entries)` (#201).
|
|
43
|
+
- **`MediaOperations.Create`** now owns a new `CmFile` in the appropriate
|
|
44
|
+
`CmFolder` before setting `InternalPath`, fixing a
|
|
45
|
+
`NullReferenceException` that made the add-a-picture/media surface
|
|
46
|
+
(`AddPicture`, `CopyToProject`, `Create`, `AddMediaFile`) unusable.
|
|
47
|
+
`CopyToProject`'s `LinkedFilesRootDir` guard now reads from `ILangProject`
|
|
48
|
+
instead of the `LcmCache` (#226).
|
|
49
|
+
- **`GetCustomFieldValue`** on a bare `ITsMultiString` (no
|
|
50
|
+
`BestAnalysisVernacularAlternative` accessor) now resolves the best
|
|
51
|
+
alternative by writing-system priority instead of failing (#224).
|
|
52
|
+
Transaction entry no longer logs a misleading "NoneType is not
|
|
53
|
+
callable" warning when the LCM `Mark` API is unavailable; it logs at
|
|
54
|
+
debug level and proceeds without rollback capability (#221).
|
|
55
|
+
- **`BaseOperations._ValidateParam`** guards against stale/deleted LCM
|
|
56
|
+
objects (`IsValidObject is False`), raising `FP_ParameterError` naming
|
|
57
|
+
the parameter instead of an opaque `NullReferenceException` deep
|
|
58
|
+
inside LCM (#205).
|
|
59
|
+
- **`SegmentOperations.GetAnalyses`** / `WfiGlossOperations` — polymorphic
|
|
60
|
+
`IAnalysis` tokens (`IWfiWordform` / `IWfiAnalysis` / `IWfiGloss` /
|
|
61
|
+
`IPunctuationForm`) are now resolved by `ClassName` rather than
|
|
62
|
+
`isinstance`, which is unreliable against base-typed LCM refs. Adds
|
|
63
|
+
`SegmentOperations.GetGloss`, `WfiAnalysisOperations.GetCategoryAbbrev`,
|
|
64
|
+
and `GetMorphemeBundles` polymorphic helpers (#212).
|
|
65
|
+
|
|
66
|
+
### Changed
|
|
67
|
+
- **`FLExProject`** singular/plural accessor aliases (e.g.
|
|
68
|
+
`InflectionFeature`/`InflectionFeatures`) are now generated from a
|
|
69
|
+
single table (`_op_aliases.py`, 58 aliases) instead of ~10 hand-written
|
|
70
|
+
properties, closing the gap where a guessed accessor name raised
|
|
71
|
+
`AttributeError`. All previously hand-written aliases are preserved;
|
|
72
|
+
each now emits a `DeprecationWarning` naming the canonical accessor
|
|
73
|
+
(#200).
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
18
77
|
## [4.1.2]
|
|
19
78
|
|
|
20
79
|
### Fixed
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyflexicon
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.2.0
|
|
4
4
|
Summary: flexicon: a user-centric Python library for FieldWorks Language Explorer (FLEx) projects via the SIL Language and Culture Model (LCM) API
|
|
5
5
|
Author-email: Matthew Lee <matthew_lee@sil.org>
|
|
6
6
|
Maintainer-email: Matthew Lee <matthew_lee@sil.org>
|
|
@@ -117,12 +117,64 @@ class EnumerableWrapper:
|
|
|
117
117
|
return f"EnumerableWrapper({self._enumerable})"
|
|
118
118
|
|
|
119
119
|
|
|
120
|
+
def _needs_enumerable_wrap(result):
|
|
121
|
+
"""
|
|
122
|
+
Decide whether `result` needs to be wrapped in EnumerableWrapper.
|
|
123
|
+
|
|
124
|
+
Historically this only checked for raw C# IEnumerable objects (those
|
|
125
|
+
exposing `GetEnumerator`). That missed two common return shapes used
|
|
126
|
+
throughout the Operations classes:
|
|
127
|
+
|
|
128
|
+
- `self.project.ObjectsIn(...)`, which already calls Python's
|
|
129
|
+
built-in `iter()` on the underlying C# enumerable before returning
|
|
130
|
+
it. The resulting object exposes `__next__`/`__iter__` but *not*
|
|
131
|
+
`GetEnumerator`, so it slipped past the old check untouched.
|
|
132
|
+
- Generator-function GetAll()/GetAnalyses() implementations (using
|
|
133
|
+
`yield`), which return a plain Python generator object -- also has
|
|
134
|
+
`__next__` but no `GetEnumerator`, and is not subscriptable and
|
|
135
|
+
has no `len()`.
|
|
136
|
+
|
|
137
|
+
Both shapes produced the exact bug in issue #201: callers got back a
|
|
138
|
+
bare iterator/generator that looked list-like but raised
|
|
139
|
+
`TypeError` on `entries[0]` or `len(entries)`.
|
|
140
|
+
|
|
141
|
+
Anything that already supports both indexing and `len()` (plain
|
|
142
|
+
lists, tuples, existing EnumerableWrapper/SmartCollection instances,
|
|
143
|
+
etc.) is left alone -- there's nothing to fix and no need to
|
|
144
|
+
re-wrap it.
|
|
145
|
+
|
|
146
|
+
Args:
|
|
147
|
+
result: The raw value returned by the wrapped method.
|
|
148
|
+
|
|
149
|
+
Returns:
|
|
150
|
+
bool: True if `result` should be wrapped in EnumerableWrapper.
|
|
151
|
+
"""
|
|
152
|
+
if result is None:
|
|
153
|
+
return False
|
|
154
|
+
if isinstance(result, EnumerableWrapper):
|
|
155
|
+
return False
|
|
156
|
+
# Raw C# IEnumerable (pythonnet objects exposing GetEnumerator).
|
|
157
|
+
if hasattr(result, "GetEnumerator"):
|
|
158
|
+
return True
|
|
159
|
+
# Plain Python iterator/generator (has __next__) that does not
|
|
160
|
+
# already behave like a sequence (no __len__ + __getitem__ pair).
|
|
161
|
+
# This covers both `iter(repo.AllInstances())` (from ObjectsIn())
|
|
162
|
+
# and generator-function bodies using `yield`.
|
|
163
|
+
if hasattr(result, "__next__"):
|
|
164
|
+
already_sequence = hasattr(result, "__len__") and hasattr(result, "__getitem__")
|
|
165
|
+
if not already_sequence:
|
|
166
|
+
return True
|
|
167
|
+
return False
|
|
168
|
+
|
|
169
|
+
|
|
120
170
|
class wrap_enumerable:
|
|
121
171
|
"""
|
|
122
|
-
Descriptor to automatically wrap IEnumerable return values.
|
|
172
|
+
Descriptor to automatically wrap IEnumerable/iterator return values.
|
|
123
173
|
|
|
124
|
-
Wraps methods that return C# IEnumerable collections
|
|
125
|
-
|
|
174
|
+
Wraps methods that return C# IEnumerable collections -- or plain
|
|
175
|
+
Python iterators/generators built on top of them (e.g. via
|
|
176
|
+
`self.project.ObjectsIn(...)` or a `yield`-based method body) -- to
|
|
177
|
+
make them Pythonic with `.Count`, `len()`, and indexing support.
|
|
126
178
|
|
|
127
179
|
Must be a descriptor to properly delegate to OperationsMethod's __get__,
|
|
128
180
|
ensuring the descriptor protocol works correctly when stacked decorators
|
|
@@ -140,6 +192,7 @@ class wrap_enumerable:
|
|
|
140
192
|
items = GetAll(project)
|
|
141
193
|
count = items.Count # Works!
|
|
142
194
|
first = items[0] # Works!
|
|
195
|
+
length = len(items) # Works!
|
|
143
196
|
"""
|
|
144
197
|
|
|
145
198
|
def __init__(self, func):
|
|
@@ -169,8 +222,7 @@ class wrap_enumerable:
|
|
|
169
222
|
# Return a wrapper that will wrap the result
|
|
170
223
|
def wrapped_method(*args, **kwargs):
|
|
171
224
|
result = inner_method(*args, **kwargs)
|
|
172
|
-
|
|
173
|
-
if result is not None and hasattr(result, "GetEnumerator"):
|
|
225
|
+
if _needs_enumerable_wrap(result):
|
|
174
226
|
return EnumerableWrapper(result)
|
|
175
227
|
return result
|
|
176
228
|
|
|
@@ -179,13 +231,12 @@ class wrap_enumerable:
|
|
|
179
231
|
def __call__(self, *args, **kwargs):
|
|
180
232
|
"""
|
|
181
233
|
Direct call support (when decorator is applied to a function, not a method).
|
|
182
|
-
|
|
234
|
+
|
|
183
235
|
This is called when the wrapped method is invoked without going through
|
|
184
236
|
the descriptor protocol (rare, but needed for some edge cases).
|
|
185
237
|
"""
|
|
186
238
|
result = self.func(*args, **kwargs)
|
|
187
|
-
|
|
188
|
-
if result is not None and hasattr(result, "GetEnumerator"):
|
|
239
|
+
if _needs_enumerable_wrap(result):
|
|
189
240
|
return EnumerableWrapper(result)
|
|
190
241
|
return result
|
|
191
242
|
|
|
@@ -1775,10 +1826,12 @@ class BaseOperations:
|
|
|
1775
1826
|
|
|
1776
1827
|
def _ValidateParam(self, param: any, param_name: str = "parameter") -> None:
|
|
1777
1828
|
"""
|
|
1778
|
-
Validate that a parameter is not None.
|
|
1829
|
+
Validate that a parameter is not None and not a stale LCM object.
|
|
1779
1830
|
|
|
1780
1831
|
This method performs a null check on a required parameter. Use this for
|
|
1781
|
-
any parameter that must be provided and non-None.
|
|
1832
|
+
any parameter that must be provided and non-None. When the parameter is
|
|
1833
|
+
a live LCM object reference, it is additionally rejected if it refers to
|
|
1834
|
+
a deleted/invalid object (``IsValidObject`` is False).
|
|
1782
1835
|
|
|
1783
1836
|
Args:
|
|
1784
1837
|
param: The parameter value to validate (any type).
|
|
@@ -1789,7 +1842,9 @@ class BaseOperations:
|
|
|
1789
1842
|
None
|
|
1790
1843
|
|
|
1791
1844
|
Raises:
|
|
1792
|
-
|
|
1845
|
+
FP_NullParameterError: If param is None.
|
|
1846
|
+
FP_ParameterError: If param is an LCM object that has been deleted
|
|
1847
|
+
or otherwise invalidated (``IsValidObject`` is False).
|
|
1793
1848
|
|
|
1794
1849
|
Example:
|
|
1795
1850
|
>>> def SetGloss(self, sense, gloss):
|
|
@@ -1811,11 +1866,25 @@ class BaseOperations:
|
|
|
1811
1866
|
|
|
1812
1867
|
Implementation Notes:
|
|
1813
1868
|
- Simple if param is None check
|
|
1869
|
+
- Stale-reference guard: a cascade-deleted LCM object keeps a live
|
|
1870
|
+
.NET reference but its Cache/Services pointers are torn down, so
|
|
1871
|
+
touching any property raises a NullReferenceException from deep
|
|
1872
|
+
inside LCM. ICmObject exposes IsValidObject (False once deleted);
|
|
1873
|
+
mirrors LCM's own guard pattern (OverridesLing_Lex.cs:1500).
|
|
1874
|
+
- getattr(..., None) means non-LCM params (str/int/dict/etc.) have
|
|
1875
|
+
no IsValidObject attribute -> None -> skipped. Only an explicit
|
|
1876
|
+
False triggers the error, so a True value is never mistaken.
|
|
1814
1877
|
- No side effects
|
|
1815
1878
|
- Exception message includes parameter name
|
|
1816
1879
|
"""
|
|
1817
1880
|
if param is None:
|
|
1818
1881
|
raise FP_NullParameterError()
|
|
1882
|
+
# Reject stale LCM object references (cascade-deleted / invalidated).
|
|
1883
|
+
# Non-LCM params return None here and are left untouched.
|
|
1884
|
+
if getattr(param, "IsValidObject", None) is False:
|
|
1885
|
+
raise FP_ParameterError(
|
|
1886
|
+
f"{param_name} refers to a deleted or invalid LCM object"
|
|
1887
|
+
)
|
|
1819
1888
|
|
|
1820
1889
|
def _ValidateParamNotEmpty(self, param: any, param_name: str = "parameter") -> None:
|
|
1821
1890
|
"""
|
|
@@ -82,7 +82,7 @@ class ConstChartOperations(BaseOperations):
|
|
|
82
82
|
"""
|
|
83
83
|
Get all constituent charts in the project.
|
|
84
84
|
|
|
85
|
-
This method returns an
|
|
85
|
+
This method returns an EnumerableWrapper (subscriptable, len()-able, lazily materialized) over all IDsConstChart objects in the
|
|
86
86
|
project database, allowing iteration over all discourse analysis charts.
|
|
87
87
|
|
|
88
88
|
Yields:
|
|
@@ -97,7 +97,7 @@ class ConstChartOperations(BaseOperations):
|
|
|
97
97
|
Mark 1 Analysis (32 rows)
|
|
98
98
|
|
|
99
99
|
Notes:
|
|
100
|
-
- Returns an
|
|
100
|
+
- Returns an EnumerableWrapper (subscriptable, len()-able) for memory efficiency; the underlying LCM enumerator is only materialized into a list on first len()/index/iteration access
|
|
101
101
|
- Charts are returned in database order
|
|
102
102
|
- Each chart can contain multiple rows for analysis
|
|
103
103
|
- Use GetName() to access the chart name
|
|
@@ -38,7 +38,7 @@ import logging
|
|
|
38
38
|
import os
|
|
39
39
|
import clr
|
|
40
40
|
|
|
41
|
-
from .Shared.string_utils import normalize_ws_handle
|
|
41
|
+
from .Shared.string_utils import normalize_ws_handle, best_multistring_alternative
|
|
42
42
|
|
|
43
43
|
clr.AddReference("System")
|
|
44
44
|
import System
|
|
@@ -2407,42 +2407,10 @@ class FLExProject(object):
|
|
|
2407
2407
|
self._constchartclausemarker_ops = ConstChartClauseMarkerOperations(self)
|
|
2408
2408
|
return self._constchartclausemarker_ops
|
|
2409
2409
|
|
|
2410
|
-
# Singular aliases for backward compatibility
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
"""Alias for Agents (singular form for backward compatibility)."""
|
|
2415
|
-
return self.Agents
|
|
2416
|
-
|
|
2417
|
-
@property
|
|
2418
|
-
def PossibilityList(self):
|
|
2419
|
-
"""Alias for PossibilityLists (singular form for backward compatibility)."""
|
|
2420
|
-
return self.PossibilityLists
|
|
2421
|
-
|
|
2422
|
-
@property
|
|
2423
|
-
def WritingSystem(self):
|
|
2424
|
-
"""Alias for WritingSystems (singular form for backward compatibility)."""
|
|
2425
|
-
return self.WritingSystems
|
|
2426
|
-
|
|
2427
|
-
@property
|
|
2428
|
-
def Overlay(self):
|
|
2429
|
-
"""Alias for Overlays (singular form for backward compatibility)."""
|
|
2430
|
-
return self.Overlays
|
|
2431
|
-
|
|
2432
|
-
@property
|
|
2433
|
-
def Publication(self):
|
|
2434
|
-
"""Alias for Publications (singular form for backward compatibility)."""
|
|
2435
|
-
return self.Publications
|
|
2436
|
-
|
|
2437
|
-
@property
|
|
2438
|
-
def TranslationType(self):
|
|
2439
|
-
"""Alias for TranslationTypes (singular form for backward compatibility)."""
|
|
2440
|
-
return self.TranslationTypes
|
|
2441
|
-
|
|
2442
|
-
@property
|
|
2443
|
-
def Note(self):
|
|
2444
|
-
"""Alias for Notes (singular form for backward compatibility)."""
|
|
2445
|
-
return self.Notes
|
|
2410
|
+
# Singular/plural aliases for backward compatibility are generated at
|
|
2411
|
+
# module scope from FLExProject._OP_NAMESPACE_ALIASES (see the end of
|
|
2412
|
+
# this module). Each generated alias emits a DeprecationWarning that
|
|
2413
|
+
# names the canonical accessor. See issue #200.
|
|
2446
2414
|
|
|
2447
2415
|
def ImportLocalizedLists(self, language_code, progress=None):
|
|
2448
2416
|
"""Deprecated. Use ``project.LocalizedLists.Import(language_code)``."""
|
|
@@ -2469,21 +2437,6 @@ class FLExProject(object):
|
|
|
2469
2437
|
progress=progress
|
|
2470
2438
|
)
|
|
2471
2439
|
|
|
2472
|
-
@property
|
|
2473
|
-
def AnnotationDef(self):
|
|
2474
|
-
"""Alias for AnnotationDefs (singular form for backward compatibility)."""
|
|
2475
|
-
return self.AnnotationDefs
|
|
2476
|
-
|
|
2477
|
-
@property
|
|
2478
|
-
def Check(self):
|
|
2479
|
-
"""Alias for Checks (singular form for backward compatibility)."""
|
|
2480
|
-
return self.Checks
|
|
2481
|
-
|
|
2482
|
-
@property
|
|
2483
|
-
def CustomField(self):
|
|
2484
|
-
"""Alias for CustomFields (singular form for backward compatibility)."""
|
|
2485
|
-
return self.CustomFields
|
|
2486
|
-
|
|
2487
2440
|
# --- General ---
|
|
2488
2441
|
|
|
2489
2442
|
def ProjectName(self):
|
|
@@ -2711,9 +2664,10 @@ class FLExProject(object):
|
|
|
2711
2664
|
"""
|
|
2712
2665
|
import os
|
|
2713
2666
|
|
|
2714
|
-
# Try to get LinkedFilesRootDir
|
|
2715
|
-
|
|
2716
|
-
|
|
2667
|
+
# Try to get LinkedFilesRootDir. It lives on ILangProject (self.lp), not
|
|
2668
|
+
# on the LcmCache (self.project) — see issue #226.
|
|
2669
|
+
if getattr(self.lp, "LinkedFilesRootDir", None):
|
|
2670
|
+
return self.lp.LinkedFilesRootDir
|
|
2717
2671
|
|
|
2718
2672
|
# Fallback: construct default path from project folder
|
|
2719
2673
|
if hasattr(self.project, "ProjectId") and hasattr(self.project.ProjectId, "ProjectFolder"):
|
|
@@ -3352,7 +3306,27 @@ class FLExProject(object):
|
|
|
3352
3306
|
WSHandle = self.__WSHandle(languageTagOrHandle, None)
|
|
3353
3307
|
return mua.get_String(WSHandle)
|
|
3354
3308
|
else:
|
|
3355
|
-
|
|
3309
|
+
# mua is a bare ITsMultiString (SIL.LCModel.Core.KernelInterfaces),
|
|
3310
|
+
# which has no BestAnalysisVernacularAlternative accessor (that
|
|
3311
|
+
# lives on IMultiAccessorBase). Reimplement the "best available
|
|
3312
|
+
# alternative" priority walk on top of get_String() instead.
|
|
3313
|
+
# See issue #224.
|
|
3314
|
+
analWsHandles = [int(ws.Handle) for ws in self.lp.CurrentAnalysisWritingSystems]
|
|
3315
|
+
vernWsHandles = [int(ws.Handle) for ws in self.lp.CurrentVernacularWritingSystems]
|
|
3316
|
+
best = best_multistring_alternative(
|
|
3317
|
+
mua,
|
|
3318
|
+
self.project.DefaultAnalWs,
|
|
3319
|
+
self.project.DefaultVernWs,
|
|
3320
|
+
analWsHandles,
|
|
3321
|
+
vernWsHandles,
|
|
3322
|
+
)
|
|
3323
|
+
if best is not None:
|
|
3324
|
+
return best
|
|
3325
|
+
# Total miss across every configured WS: still return a
|
|
3326
|
+
# (possibly empty) ITsString so this branch's contract
|
|
3327
|
+
# matches the sibling branches above/below, which always
|
|
3328
|
+
# return ITsString.
|
|
3329
|
+
return ITsString(mua.get_String(self.project.DefaultAnalWs))
|
|
3356
3330
|
|
|
3357
3331
|
elif fieldType == CellarPropertyType.Integer:
|
|
3358
3332
|
return self.project.DomainDataByFlid.get_IntProp(hvo, fieldID)
|
|
@@ -4421,3 +4395,20 @@ class FLExProject(object):
|
|
|
4421
4395
|
yield name, "\n".join(content)
|
|
4422
4396
|
else:
|
|
4423
4397
|
yield "\n".join(content)
|
|
4398
|
+
|
|
4399
|
+
|
|
4400
|
+
# ============================================================================
|
|
4401
|
+
# Operation-namespace naming aliases (issue #200)
|
|
4402
|
+
# ============================================================================
|
|
4403
|
+
# Deprecated singular/plural aliases for the operation-namespace accessors are
|
|
4404
|
+
# generated from a single table in the SIL-free ._op_aliases module and
|
|
4405
|
+
# attached here. Each alias forwards to the canonical accessor and emits a
|
|
4406
|
+
# DeprecationWarning. Keeping the table and generator SIL-free lets them be
|
|
4407
|
+
# unit-tested without FieldWorks (tests/test_flexproject_aliases.py).
|
|
4408
|
+
from ._op_aliases import ( # noqa: E402
|
|
4409
|
+
OP_NAMESPACE_ALIASES as _OP_NAMESPACE_ALIASES,
|
|
4410
|
+
install_op_namespace_aliases as _install_op_namespace_aliases,
|
|
4411
|
+
)
|
|
4412
|
+
|
|
4413
|
+
FLExProject._OP_NAMESPACE_ALIASES = _OP_NAMESPACE_ALIASES
|
|
4414
|
+
_install_op_namespace_aliases(FLExProject)
|
|
@@ -262,6 +262,7 @@ class MorphRuleOperations(BaseOperations):
|
|
|
262
262
|
return AffixTemplateCollection(wrapped)
|
|
263
263
|
return AffixTemplateCollection()
|
|
264
264
|
|
|
265
|
+
@wrap_enumerable
|
|
265
266
|
@OperationsMethod
|
|
266
267
|
def GetAllAdhocCoProhibitions(self):
|
|
267
268
|
"""
|
|
@@ -736,7 +737,7 @@ class MorphRuleOperations(BaseOperations):
|
|
|
736
737
|
# ========== DUPLICATION ==========
|
|
737
738
|
|
|
738
739
|
@OperationsMethod
|
|
739
|
-
def Duplicate(self, item_or_hvo, insert_after=True):
|
|
740
|
+
def Duplicate(self, item_or_hvo, insert_after=True, deep=True):
|
|
740
741
|
"""
|
|
741
742
|
Duplicate a morphological rule or template, creating a copy with a new GUID.
|
|
742
743
|
|
|
@@ -748,10 +749,15 @@ class MorphRuleOperations(BaseOperations):
|
|
|
748
749
|
item_or_hvo: The rule object or HVO to duplicate.
|
|
749
750
|
insert_after (bool): If True (default), insert after the source.
|
|
750
751
|
If False, insert at end of collection.
|
|
751
|
-
deep (bool): If True, copy reference sequences (slot
|
|
752
|
-
for affix templates). If False
|
|
752
|
+
deep (bool): If True (default), copy reference sequences (slot
|
|
753
|
+
assignments for affix templates). If False, only copy simple
|
|
753
754
|
properties.
|
|
754
755
|
|
|
756
|
+
Note: this default (True) matches LexEntry/Text (deep by
|
|
757
|
+
default); it differs from Media/Wordform (deep=False by
|
|
758
|
+
default). See issue #203 -- the split is deliberate per
|
|
759
|
+
object family, not an inconsistency to "fix" elsewhere.
|
|
760
|
+
|
|
755
761
|
Returns:
|
|
756
762
|
The newly created duplicate with a new GUID.
|
|
757
763
|
|
|
@@ -25,7 +25,6 @@ from SIL.LCModel import (
|
|
|
25
25
|
IMoAffixAllomorphFactory,
|
|
26
26
|
ILexEntry,
|
|
27
27
|
IPhEnvironment,
|
|
28
|
-
MoMorphTypeTags,
|
|
29
28
|
)
|
|
30
29
|
from SIL.LCModel.Core.KernelInterfaces import ITsString
|
|
31
30
|
from SIL.LCModel.Core.Text import TsStringUtils
|
|
@@ -38,6 +37,10 @@ from ..FLExProject import (
|
|
|
38
37
|
# Import string utilities
|
|
39
38
|
from ..Shared.string_utils import normalize_text
|
|
40
39
|
|
|
40
|
+
# Import shared morph-type resolution utilities (single source of truth
|
|
41
|
+
# shared with LexEntryOperations.Create -- see issue #213/#214)
|
|
42
|
+
from ..Shared.morph_type_utils import find_morph_type, is_stem_morph_type, morph_type_not_found_error
|
|
43
|
+
|
|
41
44
|
# Import wrapper classes
|
|
42
45
|
from .allomorph import Allomorph
|
|
43
46
|
from .allomorph_collection import AllomorphCollection
|
|
@@ -258,27 +261,13 @@ class AllomorphOperations(BaseOperations):
|
|
|
258
261
|
wsHandle = self.__WSHandle(wsHandle)
|
|
259
262
|
|
|
260
263
|
# Accept morph type as a string name (with or without display markers).
|
|
264
|
+
# Uses the SAME resolver as LexEntryOperations.Create (see issue #213)
|
|
265
|
+
# so '=enclitic', 'proclitic=', '-suffix', etc. all resolve identically
|
|
266
|
+
# regardless of which operations class is used.
|
|
261
267
|
if isinstance(morphType, str):
|
|
262
|
-
|
|
263
|
-
morph_types = self.project.lp.LexDbOA.MorphTypesOA
|
|
264
|
-
from ..Shared.string_utils import normalize_match_key, best_analysis_text
|
|
265
|
-
target = normalize_match_key(bare, casefold=True)
|
|
266
|
-
def _find(possibilities):
|
|
267
|
-
for mt in possibilities:
|
|
268
|
-
mt_name = best_analysis_text(mt.Name)
|
|
269
|
-
if mt_name and normalize_match_key(mt_name, casefold=True) == target:
|
|
270
|
-
return mt
|
|
271
|
-
if mt.SubPossibilitiesOS.Count > 0:
|
|
272
|
-
found = _find(mt.SubPossibilitiesOS)
|
|
273
|
-
if found:
|
|
274
|
-
return found
|
|
275
|
-
return None
|
|
276
|
-
resolved = _find(morph_types.PossibilitiesOS) if morph_types else None
|
|
268
|
+
resolved = find_morph_type(self.project, morphType)
|
|
277
269
|
if resolved is None:
|
|
278
|
-
raise FP_ParameterError(
|
|
279
|
-
f"Morph type '{morphType}' not found. "
|
|
280
|
-
"Use project.LexEntry.GetAvailableMorphTypes() to see valid names."
|
|
281
|
-
)
|
|
270
|
+
raise FP_ParameterError(morph_type_not_found_error(morphType))
|
|
282
271
|
morphType = resolved
|
|
283
272
|
|
|
284
273
|
# If no morphType provided, inherit from entry's LexemeFormOA (FLEx behavior)
|
|
@@ -1127,32 +1116,29 @@ class AllomorphOperations(BaseOperations):
|
|
|
1127
1116
|
Determine if a morph type should use MoStemAllomorph or MoAffixAllomorph.
|
|
1128
1117
|
|
|
1129
1118
|
Args:
|
|
1130
|
-
morph_type: IMoMorphType object
|
|
1119
|
+
morph_type: IMoMorphType object, or a string morph-type name.
|
|
1131
1120
|
|
|
1132
1121
|
Returns:
|
|
1133
1122
|
bool: True if stem type (uses MoStemAllomorph), False if affix type
|
|
1134
1123
|
|
|
1124
|
+
Raises:
|
|
1125
|
+
FP_ParameterError: If morph_type is a string that does not
|
|
1126
|
+
resolve to a known morph type.
|
|
1127
|
+
|
|
1135
1128
|
Notes:
|
|
1136
|
-
|
|
1129
|
+
Delegates to Shared.morph_type_utils.is_stem_morph_type -- the
|
|
1130
|
+
same classification used by LexEntryOperations.__IsStemType.
|
|
1131
|
+
Accepts a string here (in addition to an IMoMorphType object)
|
|
1132
|
+
so that callers passing a raw name never hit an AttributeError
|
|
1133
|
+
deep in this helper (see issue #213).
|
|
1137
1134
|
"""
|
|
1138
|
-
if morph_type
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
MoMorphTypeTags.kguidMorphBoundRoot,
|
|
1146
|
-
MoMorphTypeTags.kguidMorphBoundStem,
|
|
1147
|
-
MoMorphTypeTags.kguidMorphClitic,
|
|
1148
|
-
MoMorphTypeTags.kguidMorphEnclitic,
|
|
1149
|
-
MoMorphTypeTags.kguidMorphProclitic,
|
|
1150
|
-
MoMorphTypeTags.kguidMorphParticle,
|
|
1151
|
-
MoMorphTypeTags.kguidMorphPhrase,
|
|
1152
|
-
MoMorphTypeTags.kguidMorphDiscontiguousPhrase,
|
|
1153
|
-
}
|
|
1154
|
-
|
|
1155
|
-
return morph_type.Guid in stem_guids
|
|
1135
|
+
if isinstance(morph_type, str):
|
|
1136
|
+
resolved = find_morph_type(self.project, morph_type)
|
|
1137
|
+
if resolved is None:
|
|
1138
|
+
raise FP_ParameterError(morph_type_not_found_error(morph_type))
|
|
1139
|
+
morph_type = resolved
|
|
1140
|
+
|
|
1141
|
+
return is_stem_morph_type(morph_type)
|
|
1156
1142
|
|
|
1157
1143
|
def __WSHandle(self, wsHandle):
|
|
1158
1144
|
"""
|
|
@@ -29,7 +29,6 @@ from SIL.LCModel import (
|
|
|
29
29
|
IMoStemAllomorphFactory,
|
|
30
30
|
IMoAffixAllomorphFactory,
|
|
31
31
|
IMoForm,
|
|
32
|
-
MoMorphTypeTags,
|
|
33
32
|
ILexEntryRef,
|
|
34
33
|
ILexEntryRefFactory,
|
|
35
34
|
LexEntryRefTags,
|
|
@@ -48,6 +47,15 @@ from ..FLExProject import (
|
|
|
48
47
|
# Import string utilities
|
|
49
48
|
from ..Shared.string_utils import normalize_text, normalize_match_key, best_analysis_text, best_vernacular_text
|
|
50
49
|
|
|
50
|
+
# Import shared morph-type resolution utilities (single source of truth
|
|
51
|
+
# for name lookup, display-marker normalization, and stem/affix
|
|
52
|
+
# classification -- also used by AllomorphOperations)
|
|
53
|
+
from ..Shared.morph_type_utils import (
|
|
54
|
+
find_morph_type,
|
|
55
|
+
is_stem_morph_type,
|
|
56
|
+
morph_type_not_found_error,
|
|
57
|
+
)
|
|
58
|
+
|
|
51
59
|
|
|
52
60
|
class LexEntryOperations(BaseOperations):
|
|
53
61
|
"""
|
|
@@ -101,7 +109,7 @@ class LexEntryOperations(BaseOperations):
|
|
|
101
109
|
"""
|
|
102
110
|
Get all lexical entries in the project.
|
|
103
111
|
|
|
104
|
-
This method returns an
|
|
112
|
+
This method returns an EnumerableWrapper (subscriptable, len()-able, lazily materialized) over all ILexEntry objects in the
|
|
105
113
|
project database, allowing iteration over the complete lexicon.
|
|
106
114
|
|
|
107
115
|
Yields:
|
|
@@ -117,7 +125,7 @@ class LexEntryOperations(BaseOperations):
|
|
|
117
125
|
house (4 senses)
|
|
118
126
|
|
|
119
127
|
Notes:
|
|
120
|
-
- Returns an
|
|
128
|
+
- Returns an EnumerableWrapper (subscriptable, len()-able) for memory efficiency with large lexicons; the underlying LCM enumerator is only materialized into a list on first len()/index/iteration access
|
|
121
129
|
- Entries are returned in database order (not alphabetical)
|
|
122
130
|
- Use GetHeadword() to access the display form
|
|
123
131
|
- For sorted entries, use FLExProject.LexiconAllEntriesSorted()
|
|
@@ -201,9 +209,7 @@ class LexEntryOperations(BaseOperations):
|
|
|
201
209
|
# Find the morph type
|
|
202
210
|
morph_type = self.__FindMorphType(morph_type_name)
|
|
203
211
|
if morph_type is None:
|
|
204
|
-
raise FP_ParameterError(
|
|
205
|
-
f"Morph type '{morph_type_name}' not found. " f"Use one of: stem, root, prefix, suffix, infix, etc."
|
|
206
|
-
)
|
|
212
|
+
raise FP_ParameterError(morph_type_not_found_error(morph_type_name))
|
|
207
213
|
|
|
208
214
|
with self._TransactionCM(f"Create entry '{lexeme_form}'"):
|
|
209
215
|
# Create the new entry using the factory
|
|
@@ -1520,7 +1526,7 @@ class LexEntryOperations(BaseOperations):
|
|
|
1520
1526
|
if isinstance(morph_type_or_name, str):
|
|
1521
1527
|
morph_type = self.__FindMorphType(morph_type_or_name)
|
|
1522
1528
|
if morph_type is None:
|
|
1523
|
-
raise FP_ParameterError(
|
|
1529
|
+
raise FP_ParameterError(morph_type_not_found_error(morph_type_or_name))
|
|
1524
1530
|
else:
|
|
1525
1531
|
morph_type = morph_type_or_name
|
|
1526
1532
|
|
|
@@ -3129,25 +3135,11 @@ class LexEntryOperations(BaseOperations):
|
|
|
3129
3135
|
Based on FLEx logic in MorphTypeAtomicLauncher.cs
|
|
3130
3136
|
Stem types include: stem, root, bound root/stem, clitics, particles, phrases
|
|
3131
3137
|
Affix types include: prefix, suffix, infix, circumfix, etc.
|
|
3138
|
+
Delegates to Shared.morph_type_utils.is_stem_morph_type so this
|
|
3139
|
+
classification lives in exactly one place (also used by
|
|
3140
|
+
AllomorphOperations).
|
|
3132
3141
|
"""
|
|
3133
|
-
|
|
3134
|
-
return True # Default to stem
|
|
3135
|
-
|
|
3136
|
-
# Check GUID against known stem types (from MoMorphTypeTags)
|
|
3137
|
-
stem_guids = {
|
|
3138
|
-
MoMorphTypeTags.kguidMorphStem,
|
|
3139
|
-
MoMorphTypeTags.kguidMorphRoot,
|
|
3140
|
-
MoMorphTypeTags.kguidMorphBoundRoot,
|
|
3141
|
-
MoMorphTypeTags.kguidMorphBoundStem,
|
|
3142
|
-
MoMorphTypeTags.kguidMorphClitic,
|
|
3143
|
-
MoMorphTypeTags.kguidMorphEnclitic,
|
|
3144
|
-
MoMorphTypeTags.kguidMorphProclitic,
|
|
3145
|
-
MoMorphTypeTags.kguidMorphParticle,
|
|
3146
|
-
MoMorphTypeTags.kguidMorphPhrase,
|
|
3147
|
-
MoMorphTypeTags.kguidMorphDiscontiguousPhrase,
|
|
3148
|
-
}
|
|
3149
|
-
|
|
3150
|
-
return morph_type.Guid in stem_guids
|
|
3142
|
+
return is_stem_morph_type(morph_type)
|
|
3151
3143
|
|
|
3152
3144
|
def __WSHandleAnalysis(self, wsHandle):
|
|
3153
3145
|
"""
|
|
@@ -3176,28 +3168,10 @@ class LexEntryOperations(BaseOperations):
|
|
|
3176
3168
|
|
|
3177
3169
|
Returns:
|
|
3178
3170
|
IMoMorphType or None: The morph type object if found, None otherwise
|
|
3179
|
-
"""
|
|
3180
|
-
# Strip display markers (prefix '-', '=', '~'; postfix '-', '=', '~', '<', '>')
|
|
3181
|
-
# that FLEx shows in the UI but that are NOT part of the canonical IMoMorphType.Name.
|
|
3182
|
-
bare = name.strip('-=~<>')
|
|
3183
|
-
target = normalize_match_key(bare, casefold=True)
|
|
3184
|
-
wsHandle = self.project.project.DefaultAnalWs
|
|
3185
|
-
|
|
3186
|
-
morph_types = self.project.lp.LexDbOA.MorphTypesOA
|
|
3187
|
-
if morph_types is None:
|
|
3188
|
-
return None
|
|
3189
3171
|
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
|
|
3194
|
-
|
|
3195
|
-
|
|
3196
|
-
# Search subcategories
|
|
3197
|
-
if mt.SubPossibilitiesOS.Count > 0:
|
|
3198
|
-
found = search_morph_types(mt.SubPossibilitiesOS)
|
|
3199
|
-
if found:
|
|
3200
|
-
return found
|
|
3201
|
-
return None
|
|
3202
|
-
|
|
3203
|
-
return search_morph_types(morph_types.PossibilitiesOS)
|
|
3172
|
+
Notes:
|
|
3173
|
+
Delegates to Shared.morph_type_utils.find_morph_type so this is
|
|
3174
|
+
the single lookup used by both LexEntryOperations and
|
|
3175
|
+
AllomorphOperations (see issue #213/#214).
|
|
3176
|
+
"""
|
|
3177
|
+
return find_morph_type(self.project, name)
|