mixinv2 0.3.0.post58.dev0__tar.gz → 0.3.0.post65.dev0__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.
- {mixinv2-0.3.0.post58.dev0 → mixinv2-0.3.0.post65.dev0}/PKG-INFO +1 -1
- {mixinv2-0.3.0.post58.dev0 → mixinv2-0.3.0.post65.dev0}/src/mixinv2/__init__.py +1 -0
- {mixinv2-0.3.0.post58.dev0 → mixinv2-0.3.0.post65.dev0}/src/mixinv2/_core.py +20 -6
- {mixinv2-0.3.0.post58.dev0 → mixinv2-0.3.0.post65.dev0}/src/mixinv2/_runtime.py +34 -43
- {mixinv2-0.3.0.post58.dev0 → mixinv2-0.3.0.post65.dev0}/.gitignore +0 -0
- {mixinv2-0.3.0.post58.dev0 → mixinv2-0.3.0.post65.dev0}/README.md +0 -0
- {mixinv2-0.3.0.post58.dev0 → mixinv2-0.3.0.post65.dev0}/docs/Makefile +0 -0
- {mixinv2-0.3.0.post58.dev0 → mixinv2-0.3.0.post65.dev0}/docs/_static/favicon.svg +0 -0
- {mixinv2-0.3.0.post58.dev0 → mixinv2-0.3.0.post65.dev0}/docs/_static/logo.svg +0 -0
- {mixinv2-0.3.0.post58.dev0 → mixinv2-0.3.0.post65.dev0}/docs/conf.py +0 -0
- {mixinv2-0.3.0.post58.dev0 → mixinv2-0.3.0.post65.dev0}/docs/index.rst +0 -0
- {mixinv2-0.3.0.post58.dev0 → mixinv2-0.3.0.post65.dev0}/docs/installation.rst +0 -0
- {mixinv2-0.3.0.post58.dev0 → mixinv2-0.3.0.post65.dev0}/docs/mixinv2-tutorial.rst +0 -0
- {mixinv2-0.3.0.post58.dev0 → mixinv2-0.3.0.post65.dev0}/docs/specification.md +0 -0
- {mixinv2-0.3.0.post58.dev0 → mixinv2-0.3.0.post65.dev0}/docs/tutorial.rst +0 -0
- {mixinv2-0.3.0.post58.dev0 → mixinv2-0.3.0.post65.dev0}/pyproject.toml +0 -0
- {mixinv2-0.3.0.post58.dev0 → mixinv2-0.3.0.post65.dev0}/src/mixinv2/_config.py +0 -0
- {mixinv2-0.3.0.post58.dev0 → mixinv2-0.3.0.post65.dev0}/src/mixinv2/_interned_linked_list.py +0 -0
- {mixinv2-0.3.0.post58.dev0 → mixinv2-0.3.0.post65.dev0}/src/mixinv2/_mixin_directory.py +0 -0
- {mixinv2-0.3.0.post58.dev0 → mixinv2-0.3.0.post65.dev0}/src/mixinv2/_mixin_parser.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mixinv2
|
|
3
|
-
Version: 0.3.0.
|
|
3
|
+
Version: 0.3.0.post65.dev0
|
|
4
4
|
Summary: A dependency injection framework with pytest-fixture syntax, plus a configuration language for declarative programming
|
|
5
5
|
Project-URL: Repository, https://github.com/Atry/MIXINv2
|
|
6
6
|
Author-email: "Yang, Bo" <yang-bo@yang-bo.com>
|
|
@@ -44,6 +44,7 @@ from mixinv2._core import public as public
|
|
|
44
44
|
from mixinv2._core import resource as resource
|
|
45
45
|
from mixinv2._core import scope as scope
|
|
46
46
|
from mixinv2._core import Bottom as Bottom
|
|
47
|
+
from mixinv2._core import fixpoint_cached_property as fixpoint_cached_property
|
|
47
48
|
from mixinv2._runtime import evaluate as evaluate
|
|
48
49
|
|
|
49
50
|
if TYPE_CHECKING:
|
|
@@ -553,6 +553,7 @@ from typing import (
|
|
|
553
553
|
Awaitable,
|
|
554
554
|
Callable,
|
|
555
555
|
ChainMap,
|
|
556
|
+
ClassVar,
|
|
556
557
|
Collection,
|
|
557
558
|
ContextManager,
|
|
558
559
|
Final,
|
|
@@ -665,9 +666,6 @@ class Bottom(RecursionError):
|
|
|
665
666
|
self.incomplete_result = incomplete_result
|
|
666
667
|
|
|
667
668
|
|
|
668
|
-
_max_fixpoint_iterations_var: ContextVar[int] = ContextVar(
|
|
669
|
-
"_max_fixpoint_iterations_var", default=100
|
|
670
|
-
)
|
|
671
669
|
_FIXPOINT_SENTINEL = object()
|
|
672
670
|
|
|
673
671
|
# Registry of attribute names that need clearing during fixpoint digest cycles.
|
|
@@ -706,8 +704,20 @@ class fixpoint_cached_property:
|
|
|
706
704
|
@fixpoint_cached_property(bottom=lambda: defaultdict(set))
|
|
707
705
|
def qualified_this(self):
|
|
708
706
|
...
|
|
707
|
+
|
|
708
|
+
The class-level ``max_fixpoint_iterations`` ContextVar controls the
|
|
709
|
+
maximum number of digest rounds. ``0`` disables fixpoint iteration
|
|
710
|
+
and raises ``Bottom`` on reentry. Default ``100`` iterates until
|
|
711
|
+
convergence or raises ``Bottom`` if not converged::
|
|
712
|
+
|
|
713
|
+
fixpoint_cached_property.max_fixpoint_iterations.set(0) # single-pass
|
|
714
|
+
fixpoint_cached_property.max_fixpoint_iterations.set(100) # multi-pass (default)
|
|
709
715
|
"""
|
|
710
716
|
|
|
717
|
+
max_fixpoint_iterations: ClassVar[ContextVar[int]] = ContextVar(
|
|
718
|
+
"fixpoint_cached_property.max_fixpoint_iterations", default=100
|
|
719
|
+
)
|
|
720
|
+
|
|
711
721
|
def __init__(
|
|
712
722
|
self,
|
|
713
723
|
func: Callable = None,
|
|
@@ -737,6 +747,10 @@ class fixpoint_cached_property:
|
|
|
737
747
|
self.attrname = name
|
|
738
748
|
_fixpoint_clearable_attrs.add(self.attrname)
|
|
739
749
|
|
|
750
|
+
@classmethod
|
|
751
|
+
def _get_max_iterations(cls) -> int:
|
|
752
|
+
return cls.max_fixpoint_iterations.get()
|
|
753
|
+
|
|
740
754
|
def __get__(self, instance: object, owner: type = None) -> object:
|
|
741
755
|
if instance is None:
|
|
742
756
|
return self
|
|
@@ -745,7 +759,7 @@ class fixpoint_cached_property:
|
|
|
745
759
|
cache = instance.__dict__
|
|
746
760
|
value = cache.get(self.attrname)
|
|
747
761
|
if value is not None:
|
|
748
|
-
max_iterations =
|
|
762
|
+
max_iterations = self._get_max_iterations()
|
|
749
763
|
if max_iterations == 0:
|
|
750
764
|
return value
|
|
751
765
|
# Detect reentry: if this key is currently being computed
|
|
@@ -759,7 +773,7 @@ class fixpoint_cached_property:
|
|
|
759
773
|
context.add_participant(instance)
|
|
760
774
|
return value
|
|
761
775
|
|
|
762
|
-
max_iterations =
|
|
776
|
+
max_iterations = self._get_max_iterations()
|
|
763
777
|
context = _fixpoint_context_var.get()
|
|
764
778
|
instance_id = id(instance)
|
|
765
779
|
key = (instance_id, self.attrname)
|
|
@@ -895,7 +909,7 @@ class _fixpoint_dependent_property:
|
|
|
895
909
|
if value is not None:
|
|
896
910
|
return value
|
|
897
911
|
|
|
898
|
-
if
|
|
912
|
+
if fixpoint_cached_property.max_fixpoint_iterations.get() > 0:
|
|
899
913
|
# Register as participant so clear_participant_caches can
|
|
900
914
|
# invalidate this cached value between fixpoint iterations.
|
|
901
915
|
context = _fixpoint_context_var.get()
|
|
@@ -34,7 +34,6 @@ from mixinv2._core import (
|
|
|
34
34
|
HasDict,
|
|
35
35
|
OuterSentinel,
|
|
36
36
|
SymbolKind,
|
|
37
|
-
_max_fixpoint_iterations_var,
|
|
38
37
|
)
|
|
39
38
|
|
|
40
39
|
|
|
@@ -639,7 +638,6 @@ class MultiplePatcher(Patcher[TPatch_co]):
|
|
|
639
638
|
def evaluate(
|
|
640
639
|
*namespaces: "ModuleType | ScopeDefinition",
|
|
641
640
|
modules_public: bool = False,
|
|
642
|
-
max_fixpoint_iterations: int = 100,
|
|
643
641
|
) -> Scope:
|
|
644
642
|
"""
|
|
645
643
|
Resolves a Scope from the given namespaces.
|
|
@@ -653,13 +651,15 @@ def evaluate(
|
|
|
653
651
|
When multiple namespaces are provided, they are union-mounted at the root level.
|
|
654
652
|
Resources from all namespaces are merged according to the merger election algorithm.
|
|
655
653
|
|
|
654
|
+
To control fixpoint iteration, set the class-level ContextVar before calling::
|
|
655
|
+
|
|
656
|
+
from mixinv2._core import fixpoint_cached_property
|
|
657
|
+
fixpoint_cached_property.max_fixpoint_iterations.set(0) # single-pass
|
|
658
|
+
fixpoint_cached_property.max_fixpoint_iterations.set(100) # multi-pass (default)
|
|
659
|
+
|
|
656
660
|
:param namespaces: Modules or namespace definitions (decorated with @scope) to resolve.
|
|
657
661
|
:param modules_public: If True, modules are marked as public, making their submodules
|
|
658
662
|
accessible via attribute access. Defaults to False (private by default).
|
|
659
|
-
:param max_fixpoint_iterations: Maximum number of fixpoint iterations for
|
|
660
|
-
resolving cyclic dependencies. ``0`` disables fixpoint iteration and
|
|
661
|
-
raises ``Bottom`` on reentry. Default ``100`` iterates until convergence
|
|
662
|
-
or raises ``Bottom`` if not converged.
|
|
663
663
|
:return: The root Scope.
|
|
664
664
|
|
|
665
665
|
Example::
|
|
@@ -667,7 +667,6 @@ def evaluate(
|
|
|
667
667
|
root = evaluate(MyNamespace)
|
|
668
668
|
root = evaluate(Base, Override) # Union mount
|
|
669
669
|
root = evaluate(my_package, modules_public=True) # Make modules accessible
|
|
670
|
-
root = evaluate(MyNamespace, max_fixpoint_iterations=0) # No fixpoint iteration
|
|
671
670
|
|
|
672
671
|
"""
|
|
673
672
|
from dataclasses import replace
|
|
@@ -682,44 +681,36 @@ def evaluate(
|
|
|
682
681
|
)
|
|
683
682
|
|
|
684
683
|
assert namespaces, "evaluate() requires at least one namespace"
|
|
685
|
-
if max_fixpoint_iterations < 0:
|
|
686
|
-
raise ValueError(
|
|
687
|
-
f"max_fixpoint_iterations must be non-negative, got {max_fixpoint_iterations}"
|
|
688
|
-
)
|
|
689
684
|
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
if
|
|
698
|
-
definition =
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
kwargs=KwargsSentinel.STATIC, # Root is always static
|
|
715
|
-
)
|
|
685
|
+
def to_scope_definition(
|
|
686
|
+
namespace: ModuleType | ScopeDefinition,
|
|
687
|
+
) -> ScopeDefinition:
|
|
688
|
+
if isinstance(namespace, ScopeDefinition):
|
|
689
|
+
return namespace
|
|
690
|
+
if isinstance(namespace, ModuleType):
|
|
691
|
+
definition = _parse_package(namespace)
|
|
692
|
+
if modules_public:
|
|
693
|
+
return replace(definition, is_public=True)
|
|
694
|
+
return definition
|
|
695
|
+
assert_never(namespace)
|
|
696
|
+
|
|
697
|
+
definitions = tuple(to_scope_definition(namespace) for namespace in namespaces)
|
|
698
|
+
|
|
699
|
+
root_symbol = MixinSymbol(origin=definitions)
|
|
700
|
+
|
|
701
|
+
# Create a synthetic root Mixin to enable lexical scope navigation
|
|
702
|
+
# This is needed so that children of the root scope can navigate up
|
|
703
|
+
# to find parent scope dependencies (via get_mixin)
|
|
704
|
+
root_mixin = Mixin(
|
|
705
|
+
symbol=root_symbol,
|
|
706
|
+
outer=OuterSentinel.ROOT,
|
|
707
|
+
kwargs=KwargsSentinel.STATIC, # Root is always static
|
|
708
|
+
)
|
|
716
709
|
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
finally:
|
|
722
|
-
_max_fixpoint_iterations_var.reset(token)
|
|
710
|
+
# Evaluate the root mixin to get the Scope
|
|
711
|
+
result = root_mixin.evaluated
|
|
712
|
+
assert isinstance(result, Scope)
|
|
713
|
+
return result
|
|
723
714
|
|
|
724
715
|
|
|
725
716
|
# Re-export types needed by TYPE_CHECKING imports
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mixinv2-0.3.0.post58.dev0 → mixinv2-0.3.0.post65.dev0}/src/mixinv2/_interned_linked_list.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|