• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ets-labs / python-dependency-injector / 15674518278

16 Jun 2025 07:26AM UTC coverage: 94.584% (-0.04%) from 94.62%
15674518278

Pull #898

github

web-flow
Merge b414ef7e2 into f2da51e0d
Pull Request #898: Add support for Fast Stream Depends

3388 of 3582 relevant lines covered (94.58%)

0.95 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.85
/src/dependency_injector/containers.pyx
1
"""Containers module."""
1✔
2

3
import asyncio
1✔
4
import contextlib
1✔
5
import copy as copy_module
1✔
6
import json
1✔
7
import importlib
1✔
8
import inspect
1✔
9

10
try:
1✔
11
    import yaml
1✔
12
except ImportError:
×
13
    yaml = None
×
14

15
from . import providers, errors
1✔
16
from .providers cimport __is_future_or_coroutine
17
from .wiring import wire, unwire
1✔
18

19

20
class WiringConfiguration:
1✔
21
    """Container wiring configuration."""
22

23
    def __init__(self, modules=None, packages=None, from_package=None, auto_wire=True, keep_cache=False):
1✔
24
        self.modules = [*modules] if modules else []
1✔
25
        self.packages = [*packages] if packages else []
1✔
26
        self.from_package = from_package
1✔
27
        self.auto_wire = auto_wire
1✔
28
        self.keep_cache = keep_cache
1✔
29

30
    def __deepcopy__(self, memo=None):
1✔
31
        return self.__class__(self.modules, self.packages, self.from_package, self.auto_wire, self.keep_cache)
1✔
32

33

34
class Container:
1✔
35
    """Abstract container."""
36

37

38
class DynamicContainer(Container):
1✔
39
    """Dynamic inversion of control container.
40

41
    .. code-block:: python
42

43
        services = DynamicContainer()
44
        services.auth = providers.Factory(AuthService)
45
        services.users = providers.Factory(UsersService,
46
                                           auth_service=services.auth)
47

48
    .. py:attribute:: providers
49

50
        Read-only dictionary of all providers.
51

52
        :type: dict[str, :py:class:`dependency_injector.providers.Provider`]
53

54
    .. py:attribute:: overridden
55

56
        Tuple of overriding containers.
57

58
        :type: tuple[:py:class:`DynamicContainer`]
59

60
    .. py:attribute:: provider_type
61

62
        Type of providers that could be placed in container.
63

64
        :type: type
65
    """
66

67
    __IS_CONTAINER__ = True
1✔
68

69
    def __init__(self):
1✔
70
        """Initializer.
71

72
        :rtype: None
73
        """
74
        self.provider_type = providers.Provider
1✔
75
        self.providers = {}
1✔
76
        self.overridden = tuple()
1✔
77
        self.parent = None
1✔
78
        self.declarative_parent = None
1✔
79
        self.wiring_config = WiringConfiguration()
1✔
80
        self.wired_to_modules = []
1✔
81
        self.wired_to_packages = []
1✔
82
        self.__self__ = providers.Self(self)
1✔
83
        super(DynamicContainer, self).__init__()
1✔
84

85
    def __deepcopy__(self, memo):
1✔
86
        """Create and return full copy of container."""
87
        copied = memo.get(id(self))
1✔
88
        if copied is not None:
1✔
89
            return copied
×
90

91
        copied = self.__class__()
1✔
92
        memo[id(self)] = copied
1✔
93

94
        copied.__self__ = providers.deepcopy(self.__self__, memo)
1✔
95
        for name in copied.__self__.alt_names:
1✔
96
            copied.set_provider(name, copied.__self__)
1✔
97

98
        copied.provider_type = providers.Provider
1✔
99
        copied.overridden = providers.deepcopy(self.overridden, memo)
1✔
100
        copied.wiring_config = copy_module.deepcopy(self.wiring_config, memo)
1✔
101
        copied.declarative_parent = self.declarative_parent
1✔
102

103
        for name, provider in providers.deepcopy(self.providers, memo).items():
1✔
104
            copied.set_provider(name, provider)
1✔
105

106
        copied.parent = providers.deepcopy(self.parent, memo)
1✔
107

108
        return copied
1✔
109

110
    def __setattr__(self, name, value):
1✔
111
        """Set instance attribute.
112

113
        If value of attribute is provider, it will be added into providers
114
        dictionary.
115

116
        :param name: Attribute name
117
        :type name: object
118

119
        :param value: Attribute value
120
        :type value: object
121

122
        :rtype: None
123
        """
124
        if isinstance(value, providers.Provider) \
1✔
125
                and not isinstance(value, providers.Self) \
1✔
126
                and name != "parent":
1✔
127
            _check_provider_type(self, value)
1✔
128

129
            self.providers[name] = value
1✔
130

131
            if isinstance(value, providers.CHILD_PROVIDERS):
1✔
132
                value.assign_parent(self)
1✔
133

134
        super(DynamicContainer, self).__setattr__(name, value)
1✔
135

136
    def __delattr__(self, name):
1✔
137
        """Delete instance attribute.
138

139
        If value of attribute is provider, it will be deleted from providers
140
        dictionary.
141

142
        :param name: Attribute name
143
        :type name: object
144

145
        :rtype: None
146
        """
147
        if name in self.providers:
1✔
148
            del self.providers[name]
1✔
149
        super(DynamicContainer, self).__delattr__(name)
1✔
150

151
    @property
1✔
152
    def dependencies(self):
153
        """Return dependency providers dictionary.
154

155
        Dependency providers can be both of :py:class:`dependency_injector.providers.Dependency` and
156
        :py:class:`dependency_injector.providers.DependenciesContainer`.
157

158
        :rtype:
159
            dict[str, :py:class:`dependency_injector.providers.Provider`]
160
        """
161
        return {
1✔
162
            name: provider
1✔
163
            for name, provider in self.providers.items()
1✔
164
            if isinstance(provider, (providers.Dependency, providers.DependenciesContainer))
1✔
165
        }
166

167
    def traverse(self, types=None):
1✔
168
        """Return providers traversal generator."""
169
        yield from providers.traverse(*self.providers.values(), types=types)
1✔
170

171
    def set_providers(self, **providers):
1✔
172
        """Set container providers.
173

174
        :param providers: Dictionary of providers
175
        :type providers:
176
            dict[object, :py:class:`dependency_injector.providers.Provider`]
177

178
        :rtype: None
179
        """
180
        for name, provider in providers.items():
1✔
181
            setattr(self, name, provider)
1✔
182

183
    def set_provider(self, name, provider):
1✔
184
        """Set container provider.
185

186
        :param name: Provider name
187
        :type name: str
188

189
        :param provider: Provider
190
        :type provider: :py:class:`dependency_injector.providers.Provider`
191

192
        :rtype: None
193
        """
194
        setattr(self, name, provider)
1✔
195

196
    def override(self, object overriding):
1✔
197
        """Override current container by overriding container.
198

199
        :param overriding: Overriding container.
200
        :type overriding: :py:class:`DynamicContainer`
201

202
        :raise: :py:exc:`dependency_injector.errors.Error` if trying to
203
                override container by itself
204

205
        :rtype: None
206
        """
207
        if overriding is self:
1✔
208
            raise errors.Error("Container {0} could not be overridden "
1✔
209
                               "with itself".format(self))
1✔
210

211
        self.overridden += (overriding,)
1✔
212

213
        for name, provider in overriding.providers.items():
1✔
214
            try:
1✔
215
                getattr(self, name).override(provider)
1✔
216
            except AttributeError:
1✔
217
                pass
1✔
218

219
    def override_providers(self, **overriding_providers):
1✔
220
        """Override container providers.
221

222
        :param overriding_providers: Dictionary of providers
223
        :type overriding_providers:
224
            dict[str, :py:class:`dependency_injector.providers.Provider`]
225

226
        :rtype: None
227
        """
228
        overridden_providers = []
1✔
229
        for name, overriding_provider in overriding_providers.items():
1✔
230
            container_provider = getattr(self, name)
1✔
231
            container_provider.override(overriding_provider)
1✔
232
            overridden_providers.append(container_provider)
1✔
233
        return ProvidersOverridingContext(self, overridden_providers)
1✔
234

235
    def reset_last_overriding(self):
1✔
236
        """Reset last overriding provider for each container providers.
237

238
        :rtype: None
239
        """
240
        if not self.overridden:
1✔
241
            raise errors.Error("Container {0} is not overridden".format(self))
1✔
242

243
        self.overridden = self.overridden[:-1]
1✔
244

245
        for provider in self.providers.values():
1✔
246
            provider.reset_last_overriding()
1✔
247

248
    def reset_override(self):
1✔
249
        """Reset all overridings for each container providers.
250

251
        :rtype: None
252
        """
253
        self.overridden = tuple()
1✔
254

255
        for provider in self.providers.values():
1✔
256
            provider.reset_override()
1✔
257

258
    def is_auto_wiring_enabled(self):
1✔
259
        """Check if auto wiring is needed."""
260
        return self.wiring_config.auto_wire is True
1✔
261

262
    def wire(self, modules=None, packages=None, from_package=None, keep_cache=None):
1✔
263
        """Wire container providers with provided packages and modules.
264

265
        :rtype: None
266
        """
267
        if modules is None and self.wiring_config.modules:
1✔
268
            modules = self.wiring_config.modules
1✔
269
        if packages is None and self.wiring_config.packages:
1✔
270
            packages = self.wiring_config.packages
1✔
271

272
        modules = [*modules] if modules else []
1✔
273
        packages = [*packages] if packages else []
1✔
274

275
        if _any_relative_string_imports_in(modules) or _any_relative_string_imports_in(packages):
1✔
276
            if from_package is None:
1✔
277
                if self.wiring_config.from_package is not None:
1✔
278
                    from_package = self.wiring_config.from_package
1✔
279
                elif self.declarative_parent is not None \
1✔
280
                        and (self.wiring_config.modules or self.wiring_config.packages):
1✔
281
                    with contextlib.suppress(Exception):
1✔
282
                        from_package = _resolve_package_name_from_cls(self.declarative_parent)
1✔
283
                else:
284
                    with contextlib.suppress(Exception):
1✔
285
                        from_package = _resolve_calling_package_name()
1✔
286

287
        modules = _resolve_string_imports(modules, from_package)
1✔
288
        packages = _resolve_string_imports(packages, from_package)
1✔
289

290
        if not modules and not packages:
1✔
291
            return
1✔
292

293
        if keep_cache is None:
1✔
294
            keep_cache = self.wiring_config.keep_cache
1✔
295

296
        wire(
1✔
297
            container=self,
298
            modules=modules,
299
            packages=packages,
300
            keep_cache=keep_cache,
1✔
301
        )
302

303
        if modules:
1✔
304
            self.wired_to_modules.extend(modules)
1✔
305
        if packages:
1✔
306
            self.wired_to_packages.extend(packages)
1✔
307

308
    def unwire(self):
1✔
309
        """Unwire container providers from previously wired packages and modules."""
310
        unwire(
1✔
311
            modules=self.wired_to_modules,
1✔
312
            packages=self.wired_to_packages,
1✔
313
        )
314

315
        self.wired_to_modules.clear()
1✔
316
        self.wired_to_packages.clear()
1✔
317

318
    def init_resources(self):
1✔
319
        """Initialize all container resources."""
320
        futures = []
1✔
321

322
        for provider in self.traverse(types=[providers.Resource]):
1✔
323
            resource = provider.init()
1✔
324

325
            if __is_future_or_coroutine(resource):
1✔
326
                futures.append(resource)
1✔
327

328
        if futures:
1✔
329
            return asyncio.gather(*futures)
1✔
330

331
    def shutdown_resources(self):
1✔
332
        """Shutdown all container resources."""
333
        def _independent_resources(resources):
1✔
334
            for resource in resources:
1✔
335
                for other_resource in resources:
1✔
336
                    if not other_resource.initialized:
1✔
337
                        continue
1✔
338
                    if resource in other_resource.related:
1✔
339
                        break
1✔
340
                else:
341
                    yield resource
1✔
342

343
        async def _async_ordered_shutdown(resources):
1✔
344
            while any(resource.initialized for resource in resources):
1✔
345
                resources_to_shutdown = list(_independent_resources(resources))
1✔
346
                if not resources_to_shutdown:
1✔
347
                    raise RuntimeError("Unable to resolve resources shutdown order")
1✔
348
                futures = []
1✔
349
                for resource in resources_to_shutdown:
1✔
350
                    result = resource.shutdown()
1✔
351
                    if __is_future_or_coroutine(result):
1✔
352
                        futures.append(result)
1✔
353
                await asyncio.gather(*futures)
1✔
354

355
        def _sync_ordered_shutdown(resources):
1✔
356
            while any(resource.initialized for resource in resources):
1✔
357
                resources_to_shutdown = list(_independent_resources(resources))
1✔
358
                if not resources_to_shutdown:
1✔
359
                    raise RuntimeError("Unable to resolve resources shutdown order")
1✔
360
                for resource in resources_to_shutdown:
1✔
361
                    resource.shutdown()
1✔
362

363
        resources = list(self.traverse(types=[providers.Resource]))
1✔
364
        if any(resource.is_async_mode_enabled() for resource in resources):
1✔
365
            return _async_ordered_shutdown(resources)
1✔
366
        else:
367
            return _sync_ordered_shutdown(resources)
1✔
368

369
    def load_config(self):
1✔
370
        """Load configuration."""
371
        config: providers.Configuration
372
        for config in self.traverse(types=[providers.Configuration]):
1✔
373
            config.load()
1✔
374

375
    def apply_container_providers_overridings(self):
1✔
376
        """Apply container providers overridings."""
377
        for provider in self.traverse(types=[providers.Container]):
1✔
378
            provider.apply_overridings()
1✔
379

380
    def reset_singletons(self):
1✔
381
        """Reset container singletons."""
382
        for provider in self.traverse(types=[providers.BaseSingleton]):
1✔
383
            provider.reset()
1✔
384
        return SingletonResetContext(self)
1✔
385

386
    def check_dependencies(self):
1✔
387
        """Check if container dependencies are defined.
388

389
        If any dependency is undefined, raises an error.
390
        """
391
        undefined = [
1✔
392
            dependency
1✔
393
            for dependency in self.traverse(types=[providers.Dependency])
1✔
394
            if not dependency.is_defined
1✔
395
        ]
396

397
        if not undefined:
1✔
398
            return
1✔
399

400
        container_name = self.parent_name if self.parent_name else self.__class__.__name__
1✔
401
        undefined_names = [
1✔
402
            f"\"{dependency.parent_name if dependency.parent_name else dependency}\""
1✔
403
            for dependency in undefined
1✔
404
        ]
405
        raise errors.Error(
1✔
406
            f"Container \"{container_name}\" has undefined dependencies: "
1✔
407
            f"{', '.join(undefined_names)}",
1✔
408
        )
409

410
    def from_schema(self, schema):
1✔
411
        """Build container providers from schema."""
412
        from .schema import build_schema
1✔
413
        for name, provider in build_schema(schema).items():
1✔
414
            self.set_provider(name, provider)
1✔
415

416
    def from_yaml_schema(self, filepath, loader=None):
1✔
417
        """Build container providers from YAML schema.
418

419
        You can specify type of loader as a second argument. By default, method
420
        uses ``SafeLoader``.
421
        """
422
        if yaml is None:
1✔
423
            raise errors.Error(
1✔
424
                "Unable to load yaml schema - PyYAML is not installed. "
425
                "Install PyYAML or install Dependency Injector with yaml extras: "
426
                "\"pip install dependency-injector[yaml]\""
427
            )
428

429
        if loader is None:
1✔
430
            loader = yaml.SafeLoader
1✔
431

432
        with open(filepath) as file:
1✔
433
            schema = yaml.load(file, loader)
1✔
434

435
        self.from_schema(schema)
1✔
436

437
    def from_json_schema(self, filepath):
1✔
438
        """Build container providers from JSON schema."""
439
        with open(filepath) as file:
1✔
440
            schema = json.load(file)
1✔
441
        self.from_schema(schema)
1✔
442

443
    def resolve_provider_name(self, provider):
1✔
444
        """Try to resolve provider name."""
445
        for provider_name, container_provider in self.providers.items():
1✔
446
            if container_provider is provider:
1✔
447
                return provider_name
1✔
448
        else:
449
            raise errors.Error(f"Can not resolve name for provider \"{provider}\"")
1✔
450

451
    @property
1✔
452
    def parent_name(self):
453
        """Return parent name."""
454
        if self.parent:
1✔
455
            return self.parent.parent_name
1✔
456

457
        if self.declarative_parent:
1✔
458
            return self.declarative_parent.__name__
1✔
459

460
        return None
1✔
461

462
    def assign_parent(self, parent):
1✔
463
        """Assign parent."""
464
        self.parent = parent
1✔
465

466

467
class DeclarativeContainerMetaClass(type):
1✔
468
    """Declarative inversion of control container meta class."""
469

470
    def __new__(type mcs, str class_name, tuple bases, dict attributes):
1✔
471
        """Declarative container class factory."""
472
        self = mcs.__fetch_self(attributes)
1✔
473
        if self is None:
1✔
474
            self = providers.Self()
1✔
475

476
        containers = {
1✔
477
            name: container
1✔
478
            for name, container in attributes.items()
1✔
479
            if is_container(container)
1✔
480
        }
481

482
        cls_providers = {
1✔
483
            name: provider
1✔
484
            for name, provider in attributes.items()
1✔
485
            if isinstance(provider, providers.Provider) and not isinstance(provider, providers.Self)
1✔
486
        }
487

488
        inherited_providers = {
1✔
489
            name: provider
1✔
490
            for base in bases
1✔
491
            if is_container(base) and base is not DynamicContainer
1✔
492
            for name, provider in base.providers.items()
1✔
493
        }
494

495
        all_providers = {}
1✔
496
        all_providers.update(inherited_providers)
1✔
497
        all_providers.update(cls_providers)
1✔
498

499
        wiring_config = attributes.get("wiring_config")
1✔
500
        if wiring_config is None:
1✔
501
            wiring_config = WiringConfiguration()
1✔
502
        if wiring_config is not None and not isinstance(wiring_config, WiringConfiguration):
1✔
503
            raise errors.Error(
×
504
                "Wiring configuration should be an instance of WiringConfiguration, "
505
                "instead got {0}".format(wiring_config)
×
506
            )
507

508
        attributes["containers"] = containers
1✔
509
        attributes["inherited_providers"] = inherited_providers
1✔
510
        attributes["cls_providers"] = cls_providers
1✔
511
        attributes["providers"] = all_providers
1✔
512
        attributes["wiring_config"] = wiring_config
1✔
513

514
        cls = <type>type.__new__(mcs, class_name, bases, attributes)
1✔
515

516
        self.set_container(cls)
1✔
517
        cls.__self__ = self
1✔
518

519
        for provider in cls.providers.values():
1✔
520
            _check_provider_type(cls, provider)
1✔
521

522
        for provider in cls.cls_providers.values():
1✔
523
            if isinstance(provider, providers.CHILD_PROVIDERS):
1✔
524
                provider.assign_parent(cls)
1✔
525

526
        return cls
1✔
527

528
    def __setattr__(cls, name, value):
1✔
529
        """Set class attribute.
530

531
        If value of attribute is provider, it will be added into providers
532
        dictionary.
533

534
        :param name: Attribute name
535
        :type name: object
536

537
        :param value: Attribute value
538
        :type value: object
539

540
        :rtype: None
541
        """
542
        if isinstance(value, providers.Provider) and name != "__self__":
1✔
543
            _check_provider_type(cls, value)
1✔
544

545
            if isinstance(value, providers.CHILD_PROVIDERS):
1✔
546
                value.assign_parent(cls)
1✔
547

548
            cls.providers[name] = value
1✔
549
            cls.cls_providers[name] = value
1✔
550
        super(DeclarativeContainerMetaClass, cls).__setattr__(name, value)
1✔
551

552
    def __delattr__(cls, name):
1✔
553
        """Delete class attribute.
554

555
        If value of attribute is provider, it will be deleted from providers
556
        dictionary.
557

558
        :param name: Attribute name
559
        :type name: object
560

561
        :rtype: None
562
        """
563
        if name in cls.providers and name in cls.cls_providers:
1✔
564
            del cls.providers[name]
1✔
565
            del cls.cls_providers[name]
1✔
566
        super(DeclarativeContainerMetaClass, cls).__delattr__(name)
1✔
567

568
    @property
1✔
569
    def dependencies(cls):
570
        """Return dependency providers dictionary.
571

572
        Dependency providers can be both of :py:class:`dependency_injector.providers.Dependency` and
573
        :py:class:`dependency_injector.providers.DependenciesContainer`.
574

575
        :rtype:
576
            dict[str, :py:class:`dependency_injector.providers.Provider`]
577
        """
578
        return {
1✔
579
            name: provider
1✔
580
            for name, provider in cls.providers.items()
1✔
581
            if isinstance(provider, (providers.Dependency, providers.DependenciesContainer))
1✔
582
        }
583

584
    def traverse(cls, types=None):
1✔
585
        """Return providers traversal generator."""
586
        yield from providers.traverse(*cls.providers.values(), types=types)
1✔
587

588
    def resolve_provider_name(cls, provider):
1✔
589
        """Try to resolve provider name."""
590
        for provider_name, container_provider in cls.providers.items():
1✔
591
            if container_provider is provider:
1✔
592
                return provider_name
1✔
593
        else:
594
            raise errors.Error(f"Can not resolve name for provider \"{provider}\"")
1✔
595

596
    @property
1✔
597
    def parent_name(cls):
598
        """Return parent name."""
599
        return cls.__name__
1✔
600

601
    @staticmethod
1✔
602
    def __fetch_self(attributes):
603
        self = None
1✔
604
        alt_names = []
1✔
605

606
        for name, value in attributes.items():
1✔
607
            if not isinstance(value, providers.Self):
1✔
608
                continue
1✔
609

610
            if self is not None and value is not self:
1✔
611
                raise errors.Error("Container can have only one \"Self\" provider")
1✔
612

613
            if name != "__self__":
1✔
614
                alt_names.append(name)
1✔
615

616
            self = value
1✔
617

618
        if self:
1✔
619
            self.set_alt_names(alt_names)
1✔
620

621
        return self
1✔
622

623

624
class DeclarativeContainer(Container, metaclass=DeclarativeContainerMetaClass):
1✔
625
    """Declarative inversion of control container.
626

627
    .. code-block:: python
628

629
        class Services(DeclarativeContainer):
630
            auth = providers.Factory(AuthService)
631
            users = providers.Factory(UsersService,
632
                                      auth_service=auth)
633
    """
634

635
    __IS_CONTAINER__ = True
1✔
636

637
    provider_type = providers.Provider
1✔
638
    """Type of providers that could be placed in container.
639

640
    :type: type
641
    """
642

643
    instance_type = DynamicContainer
1✔
644
    """Type of container that is returned on instantiating declarative
645
    container.
646

647
    :type: type
648
    """
649

650
    containers = dict()
1✔
651
    """Read-only dictionary of all nested containers.
652

653
    :type: dict[str, :py:class:`DeclarativeContainer`]
654
    """
655

656
    providers = dict()
1✔
657
    """Read-only dictionary of all providers.
658

659
    :type: dict[str, :py:class:`dependency_injector.providers.Provider`]
660
    """
661

662
    wiring_config = WiringConfiguration()
1✔
663
    """Wiring configuration.
664

665
    :type: WiringConfiguration
666
    """
667

668
    auto_load_config = True
1✔
669
    """Automatically load configuration when the container is created.
670

671
    :type: bool
672
    """
673

674
    cls_providers = dict()
1✔
675
    """Read-only dictionary of current container providers.
676

677
    :type: dict[str, :py:class:`dependency_injector.providers.Provider`]
678
    """
679

680
    inherited_providers = dict()
1✔
681
    """Read-only dictionary of inherited providers.
682

683
    :type: dict[str, :py:class:`dependency_injector.providers.Provider`]
684
    """
685

686
    overridden = tuple()
1✔
687
    """Tuple of overriding containers.
688

689
    :type: tuple[:py:class:`DeclarativeContainer`]
690
    """
691

692
    __self__ = None
1✔
693
    """Provider that provides current container.
694

695
    :type: :py:class:`dependency_injector.providers.Provider`
696
    """
697

698
    def __new__(cls, **overriding_providers):
1✔
699
        """Constructor.
700

701
        :return: Dynamic container with copy of all providers.
702
        :rtype: :py:class:`DynamicContainer`
703
        """
704
        container = cls.instance_type()
1✔
705
        container.provider_type = cls.provider_type
1✔
706
        container.wiring_config = copy_module.deepcopy(cls.wiring_config)
1✔
707
        container.declarative_parent = cls
1✔
708

709
        copied_providers = providers.deepcopy({ **cls.providers, **{"@@self@@": cls.__self__}})
1✔
710
        copied_self = copied_providers.pop("@@self@@")
1✔
711
        copied_self.set_container(container)
1✔
712

713
        container.__self__ = copied_self
1✔
714
        for name in copied_self.alt_names:
1✔
715
            container.set_provider(name, copied_self)
1✔
716

717
        for name, provider in copied_providers.items():
1✔
718
            container.set_provider(name, provider)
1✔
719

720
        if cls.auto_load_config:
1✔
721
            container.load_config()
1✔
722

723
        container.override_providers(**overriding_providers)
1✔
724
        container.apply_container_providers_overridings()
1✔
725

726
        if container.is_auto_wiring_enabled():
1✔
727
            container.wire()
1✔
728

729
        return container
1✔
730

731
    @classmethod
1✔
732
    def override(cls, object overriding):
733
        """Override current container by overriding container.
734

735
        :param overriding: Overriding container.
736
        :type overriding: :py:class:`DeclarativeContainer`
737

738
        :raise: :py:exc:`dependency_injector.errors.Error` if trying to
739
                override container by itself or its subclasses
740

741
        :rtype: None
742
        """
743
        if issubclass(cls, overriding):
1✔
744
            raise errors.Error("Container {0} could not be overridden "
1✔
745
                               "with itself or its subclasses".format(cls))
1✔
746

747
        cls.overridden += (overriding,)
1✔
748

749
        for name, provider in overriding.cls_providers.items():
1✔
750
            try:
1✔
751
                getattr(cls, name).override(provider)
1✔
752
            except AttributeError:
1✔
753
                pass
1✔
754

755
    @classmethod
1✔
756
    def reset_last_overriding(cls):
757
        """Reset last overriding provider for each container providers.
758

759
        :rtype: None
760
        """
761
        if not cls.overridden:
1✔
762
            raise errors.Error("Container {0} is not overridden".format(cls))
1✔
763

764
        cls.overridden = cls.overridden[:-1]
1✔
765

766
        for provider in cls.providers.values():
1✔
767
            provider.reset_last_overriding()
1✔
768

769
    @classmethod
1✔
770
    def reset_override(cls):
771
        """Reset all overridings for each container providers.
772

773
        :rtype: None
774
        """
775
        cls.overridden = tuple()
1✔
776

777
        for provider in cls.providers.values():
1✔
778
            provider.reset_override()
1✔
779

780

781
class SingletonResetContext:
1✔
782

783
    def __init__(self, container):
1✔
784
        self._container = container
1✔
785

786
    def __enter__(self):
1✔
787
        return self._container
1✔
788

789
    def __exit__(self, *_):
1✔
790
        self._container.reset_singletons()
1✔
791

792

793

794
class ProvidersOverridingContext:
1✔
795

796
    def __init__(self, container, overridden_providers):
1✔
797
        self._container = container
1✔
798
        self._overridden_providers = overridden_providers
1✔
799

800
    def __enter__(self):
1✔
801
        return self._container
1✔
802

803
    def __exit__(self, *_):
1✔
804
        for provider in self._overridden_providers:
1✔
805
            provider.reset_last_overriding()
1✔
806

807

808
def override(object container):
1✔
809
    """:py:class:`DeclarativeContainer` overriding decorator.
810

811
    :param container: Container that should be overridden by decorated
812
                      container.
813
    :type container: :py:class:`DeclarativeContainer`
814

815
    :return: Declarative container overriding decorator.
816
    :rtype: callable(:py:class:`DeclarativeContainer`)
817
    """
818
    def _decorator(object overriding_container):
1✔
819
        """Overriding decorator."""
820
        container.override(overriding_container)
1✔
821
        return overriding_container
1✔
822
    return _decorator
1✔
823

824

825
def copy(object base_container):
1✔
826
    """:py:class:`DeclarativeContainer` copying decorator.
827

828
    This decorator copies all providers from provided container to decorated one.
829
    If one of the decorated container providers matches to source container
830
    providers by name, it would be replaced by reference.
831

832
    :param base_container: Container that should be copied by decorated container.
833
    :type base_container: :py:class:`DeclarativeContainer`
834

835
    :return: Declarative container copying decorator.
836
    :rtype: callable(:py:class:`DeclarativeContainer`)
837
    """
838
    def _get_memo_for_matching_names(new_providers, base_providers):
1✔
839
        memo = {}
1✔
840
        for new_provider_name, new_provider in new_providers.items():
1✔
841
            if new_provider_name not in base_providers:
1✔
842
                continue
1✔
843
            source_provider = base_providers[new_provider_name]
1✔
844
            memo[id(source_provider)] = new_provider
1✔
845

846
            if hasattr(new_provider, "providers") and hasattr(source_provider, "providers"):
1✔
847
                sub_memo = _get_memo_for_matching_names(new_provider.providers, source_provider.providers)
1✔
848
                memo.update(sub_memo)
1✔
849
        return memo
1✔
850

851
    def _decorator(new_container):
1✔
852
        memo = {}
1✔
853
        memo.update(_get_memo_for_matching_names(new_container.cls_providers, base_container.providers))
1✔
854

855
        new_providers = {}
1✔
856
        new_providers.update(providers.deepcopy(base_container.providers, memo))
1✔
857
        new_providers.update(providers.deepcopy(new_container.cls_providers, memo))
1✔
858

859
        for name, provider in new_providers.items():
1✔
860
            setattr(new_container, name, provider)
1✔
861
        return new_container
1✔
862

863
    return _decorator
1✔
864

865

866
cpdef bint is_container(object instance):
1✔
867
    """Check if instance is container instance.
868

869
    :param instance: Instance to be checked.
870
    :type instance: object
871

872
    :rtype: bool
873
    """
874
    return getattr(instance, "__IS_CONTAINER__", False) is True
1✔
875

876

877
cpdef object _check_provider_type(object container, object provider):
1✔
878
    if not isinstance(provider, container.provider_type):
1✔
879
        raise errors.Error("{0} can contain only {1} "
1✔
880
                           "instances".format(container, container.provider_type))
1✔
881

882

883
cpdef bint _any_relative_string_imports_in(object modules):
1✔
884
    for module in modules:
1✔
885
        if not isinstance(module, str):
1✔
886
            continue
1✔
887
        if module.startswith("."):
1✔
888
            return True
1✔
889
    else:
890
        return False
1✔
891

892

893
cpdef list _resolve_string_imports(object modules, object from_package):
1✔
894
    return [
1✔
895
        importlib.import_module(module, from_package) if isinstance(module, str) else module
1✔
896
        for module in modules
1✔
897
    ]
898

899

900
cpdef object _resolve_calling_package_name():
1✔
901
    stack = inspect.stack()
1✔
902
    pre_last_frame = stack[0]
1✔
903
    module = inspect.getmodule(pre_last_frame[0])
1✔
904
    return module.__package__
1✔
905

906

907
cpdef object _resolve_package_name_from_cls(cls):
1✔
908
    module = importlib.import_module(cls.__module__)
1✔
909
    return module.__package__
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc