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

ets-labs / python-dependency-injector / 15094271664

18 May 2025 08:54AM UTC coverage: 94.498% (-0.03%) from 94.523%
15094271664

push

github

ZipFile
Drop Python 3.7 support

2 of 2 new or added lines in 1 file covered. (100.0%)

14 existing lines in 4 files now uncovered.

3332 of 3526 relevant lines covered (94.5%)

0.94 hits per line

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

97.92
/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):
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

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

32

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

36

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

40
    .. code-block:: python
41

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

47
    .. py:attribute:: providers
48

49
        Read-only dictionary of all providers.
50

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

53
    .. py:attribute:: overridden
54

55
        Tuple of overriding containers.
56

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

59
    .. py:attribute:: provider_type
60

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

63
        :type: type
64
    """
65

66
    __IS_CONTAINER__ = True
1✔
67

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

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

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

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

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

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

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

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

107
        return copied
1✔
108

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

292
        wire(
1✔
293
            container=self,
294
            modules=modules,
295
            packages=packages,
1✔
296
        )
297

298
        if modules:
1✔
299
            self.wired_to_modules.extend(modules)
1✔
300
        if packages:
1✔
301
            self.wired_to_packages.extend(packages)
1✔
302

303
    def unwire(self):
1✔
304
        """Unwire container providers from previously wired packages and modules."""
305
        unwire(
1✔
306
            modules=self.wired_to_modules,
1✔
307
            packages=self.wired_to_packages,
1✔
308
        )
309

310
        self.wired_to_modules.clear()
1✔
311
        self.wired_to_packages.clear()
1✔
312

313
    def init_resources(self):
1✔
314
        """Initialize all container resources."""
315
        futures = []
1✔
316

317
        for provider in self.traverse(types=[providers.Resource]):
1✔
318
            resource = provider.init()
1✔
319

UNCOV
320
            if __is_future_or_coroutine(resource):
×
UNCOV
321
                futures.append(resource)
×
322

UNCOV
323
        if futures:
×
UNCOV
324
            return asyncio.gather(*futures)
×
325

326
    def shutdown_resources(self):
1✔
327
        """Shutdown all container resources."""
328
        def _independent_resources(resources):
1✔
329
            for resource in resources:
1✔
330
                for other_resource in resources:
1✔
331
                    if not other_resource.initialized:
1✔
332
                        continue
1✔
333
                    if resource in other_resource.related:
1✔
334
                        break
1✔
335
                else:
336
                    yield resource
1✔
337

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

350
        def _sync_ordered_shutdown(resources):
1✔
351
            while any(resource.initialized for resource in resources):
1✔
352
                resources_to_shutdown = list(_independent_resources(resources))
1✔
353
                if not resources_to_shutdown:
1✔
354
                    raise RuntimeError("Unable to resolve resources shutdown order")
1✔
355
                for resource in resources_to_shutdown:
1✔
356
                    resource.shutdown()
1✔
357

358
        resources = list(self.traverse(types=[providers.Resource]))
1✔
359
        if any(resource.is_async_mode_enabled() for resource in resources):
1✔
360
            return _async_ordered_shutdown(resources)
1✔
361
        else:
362
            return _sync_ordered_shutdown(resources)
1✔
363

364
    def load_config(self):
1✔
365
        """Load configuration."""
366
        config: providers.Configuration
367
        for config in self.traverse(types=[providers.Configuration]):
1✔
368
            config.load()
1✔
369

370
    def apply_container_providers_overridings(self):
1✔
371
        """Apply container providers overridings."""
372
        for provider in self.traverse(types=[providers.Container]):
1✔
373
            provider.apply_overridings()
1✔
374

375
    def reset_singletons(self):
1✔
376
        """Reset container singletons."""
377
        for provider in self.traverse(types=[providers.BaseSingleton]):
1✔
378
            provider.reset()
1✔
379
        return SingletonResetContext(self)
1✔
380

381
    def check_dependencies(self):
1✔
382
        """Check if container dependencies are defined.
383

384
        If any dependency is undefined, raises an error.
385
        """
386
        undefined = [
1✔
387
            dependency
1✔
388
            for dependency in self.traverse(types=[providers.Dependency])
1✔
389
            if not dependency.is_defined
1✔
390
        ]
391

392
        if not undefined:
1✔
393
            return
1✔
394

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

405
    def from_schema(self, schema):
1✔
406
        """Build container providers from schema."""
407
        from .schema import build_schema
1✔
408
        for name, provider in build_schema(schema).items():
1✔
409
            self.set_provider(name, provider)
1✔
410

411
    def from_yaml_schema(self, filepath, loader=None):
1✔
412
        """Build container providers from YAML schema.
413

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

424
        if loader is None:
1✔
425
            loader = yaml.SafeLoader
1✔
426

427
        with open(filepath) as file:
1✔
428
            schema = yaml.load(file, loader)
1✔
429

430
        self.from_schema(schema)
1✔
431

432
    def from_json_schema(self, filepath):
1✔
433
        """Build container providers from JSON schema."""
434
        with open(filepath) as file:
1✔
435
            schema = json.load(file)
1✔
436
        self.from_schema(schema)
1✔
437

438
    def resolve_provider_name(self, provider):
1✔
439
        """Try to resolve provider name."""
440
        for provider_name, container_provider in self.providers.items():
1✔
441
            if container_provider is provider:
1✔
442
                return provider_name
1✔
443
        else:
444
            raise errors.Error(f"Can not resolve name for provider \"{provider}\"")
1✔
445

446
    @property
1✔
447
    def parent_name(self):
448
        """Return parent name."""
449
        if self.parent:
1✔
450
            return self.parent.parent_name
1✔
451

452
        if self.declarative_parent:
1✔
453
            return self.declarative_parent.__name__
1✔
454

455
        return None
1✔
456

457
    def assign_parent(self, parent):
1✔
458
        """Assign parent."""
459
        self.parent = parent
1✔
460

461

462
class DeclarativeContainerMetaClass(type):
1✔
463
    """Declarative inversion of control container meta class."""
464

465
    def __new__(type mcs, str class_name, tuple bases, dict attributes):
1✔
466
        """Declarative container class factory."""
467
        self = mcs.__fetch_self(attributes)
1✔
468
        if self is None:
1✔
469
            self = providers.Self()
1✔
470

471
        containers = {
1✔
472
            name: container
1✔
473
            for name, container in attributes.items()
1✔
474
            if is_container(container)
1✔
475
        }
476

477
        cls_providers = {
1✔
478
            name: provider
1✔
479
            for name, provider in attributes.items()
1✔
480
            if isinstance(provider, providers.Provider) and not isinstance(provider, providers.Self)
1✔
481
        }
482

483
        inherited_providers = {
1✔
484
            name: provider
1✔
485
            for base in bases
1✔
486
            if is_container(base) and base is not DynamicContainer
1✔
487
            for name, provider in base.providers.items()
1✔
488
        }
489

490
        all_providers = {}
1✔
491
        all_providers.update(inherited_providers)
1✔
492
        all_providers.update(cls_providers)
1✔
493

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

503
        attributes["containers"] = containers
1✔
504
        attributes["inherited_providers"] = inherited_providers
1✔
505
        attributes["cls_providers"] = cls_providers
1✔
506
        attributes["providers"] = all_providers
1✔
507
        attributes["wiring_config"] = wiring_config
1✔
508

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

511
        self.set_container(cls)
1✔
512
        cls.__self__ = self
1✔
513

514
        for provider in cls.providers.values():
1✔
515
            _check_provider_type(cls, provider)
1✔
516

517
        for provider in cls.cls_providers.values():
1✔
518
            if isinstance(provider, providers.CHILD_PROVIDERS):
1✔
519
                provider.assign_parent(cls)
1✔
520

521
        return cls
1✔
522

523
    def __setattr__(cls, name, value):
1✔
524
        """Set class attribute.
525

526
        If value of attribute is provider, it will be added into providers
527
        dictionary.
528

529
        :param name: Attribute name
530
        :type name: object
531

532
        :param value: Attribute value
533
        :type value: object
534

535
        :rtype: None
536
        """
537
        if isinstance(value, providers.Provider) and name != "__self__":
1✔
538
            _check_provider_type(cls, value)
1✔
539

540
            if isinstance(value, providers.CHILD_PROVIDERS):
1✔
541
                value.assign_parent(cls)
1✔
542

543
            cls.providers[name] = value
1✔
544
            cls.cls_providers[name] = value
1✔
545
        super(DeclarativeContainerMetaClass, cls).__setattr__(name, value)
1✔
546

547
    def __delattr__(cls, name):
1✔
548
        """Delete class attribute.
549

550
        If value of attribute is provider, it will be deleted from providers
551
        dictionary.
552

553
        :param name: Attribute name
554
        :type name: object
555

556
        :rtype: None
557
        """
558
        if name in cls.providers and name in cls.cls_providers:
1✔
559
            del cls.providers[name]
1✔
560
            del cls.cls_providers[name]
1✔
561
        super(DeclarativeContainerMetaClass, cls).__delattr__(name)
1✔
562

563
    @property
1✔
564
    def dependencies(cls):
565
        """Return dependency providers dictionary.
566

567
        Dependency providers can be both of :py:class:`dependency_injector.providers.Dependency` and
568
        :py:class:`dependency_injector.providers.DependenciesContainer`.
569

570
        :rtype:
571
            dict[str, :py:class:`dependency_injector.providers.Provider`]
572
        """
573
        return {
1✔
574
            name: provider
1✔
575
            for name, provider in cls.providers.items()
1✔
576
            if isinstance(provider, (providers.Dependency, providers.DependenciesContainer))
1✔
577
        }
578

579
    def traverse(cls, types=None):
1✔
580
        """Return providers traversal generator."""
581
        yield from providers.traverse(*cls.providers.values(), types=types)
1✔
582

583
    def resolve_provider_name(cls, provider):
1✔
584
        """Try to resolve provider name."""
585
        for provider_name, container_provider in cls.providers.items():
1✔
586
            if container_provider is provider:
1✔
587
                return provider_name
1✔
588
        else:
589
            raise errors.Error(f"Can not resolve name for provider \"{provider}\"")
1✔
590

591
    @property
1✔
592
    def parent_name(cls):
593
        """Return parent name."""
594
        return cls.__name__
1✔
595

596
    @staticmethod
1✔
597
    def __fetch_self(attributes):
598
        self = None
1✔
599
        alt_names = []
1✔
600

601
        for name, value in attributes.items():
1✔
602
            if not isinstance(value, providers.Self):
1✔
603
                continue
1✔
604

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

608
            if name != "__self__":
1✔
609
                alt_names.append(name)
1✔
610

611
            self = value
1✔
612

613
        if self:
1✔
614
            self.set_alt_names(alt_names)
1✔
615

616
        return self
1✔
617

618

619
class DeclarativeContainer(Container, metaclass=DeclarativeContainerMetaClass):
1✔
620
    """Declarative inversion of control container.
621

622
    .. code-block:: python
623

624
        class Services(DeclarativeContainer):
625
            auth = providers.Factory(AuthService)
626
            users = providers.Factory(UsersService,
627
                                      auth_service=auth)
628
    """
629

630
    __IS_CONTAINER__ = True
1✔
631

632
    provider_type = providers.Provider
1✔
633
    """Type of providers that could be placed in container.
634

635
    :type: type
636
    """
637

638
    instance_type = DynamicContainer
1✔
639
    """Type of container that is returned on instantiating declarative
640
    container.
641

642
    :type: type
643
    """
644

645
    containers = dict()
1✔
646
    """Read-only dictionary of all nested containers.
647

648
    :type: dict[str, :py:class:`DeclarativeContainer`]
649
    """
650

651
    providers = dict()
1✔
652
    """Read-only dictionary of all providers.
653

654
    :type: dict[str, :py:class:`dependency_injector.providers.Provider`]
655
    """
656

657
    wiring_config = WiringConfiguration()
1✔
658
    """Wiring configuration.
659

660
    :type: WiringConfiguration
661
    """
662

663
    auto_load_config = True
1✔
664
    """Automatically load configuration when the container is created.
665

666
    :type: bool
667
    """
668

669
    cls_providers = dict()
1✔
670
    """Read-only dictionary of current container providers.
671

672
    :type: dict[str, :py:class:`dependency_injector.providers.Provider`]
673
    """
674

675
    inherited_providers = dict()
1✔
676
    """Read-only dictionary of inherited providers.
677

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

681
    overridden = tuple()
1✔
682
    """Tuple of overriding containers.
683

684
    :type: tuple[:py:class:`DeclarativeContainer`]
685
    """
686

687
    __self__ = None
1✔
688
    """Provider that provides current container.
689

690
    :type: :py:class:`dependency_injector.providers.Provider`
691
    """
692

693
    def __new__(cls, **overriding_providers):
1✔
694
        """Constructor.
695

696
        :return: Dynamic container with copy of all providers.
697
        :rtype: :py:class:`DynamicContainer`
698
        """
699
        container = cls.instance_type()
1✔
700
        container.provider_type = cls.provider_type
1✔
701
        container.wiring_config = copy_module.deepcopy(cls.wiring_config)
1✔
702
        container.declarative_parent = cls
1✔
703

704
        copied_providers = providers.deepcopy({ **cls.providers, **{"@@self@@": cls.__self__}})
1✔
705
        copied_self = copied_providers.pop("@@self@@")
1✔
706
        copied_self.set_container(container)
1✔
707

708
        container.__self__ = copied_self
1✔
709
        for name in copied_self.alt_names:
1✔
710
            container.set_provider(name, copied_self)
1✔
711

712
        for name, provider in copied_providers.items():
1✔
713
            container.set_provider(name, provider)
1✔
714

715
        if cls.auto_load_config:
1✔
716
            container.load_config()
1✔
717

718
        container.override_providers(**overriding_providers)
1✔
719
        container.apply_container_providers_overridings()
1✔
720

721
        if container.is_auto_wiring_enabled():
1✔
722
            container.wire()
1✔
723

724
        return container
1✔
725

726
    @classmethod
1✔
727
    def override(cls, object overriding):
728
        """Override current container by overriding container.
729

730
        :param overriding: Overriding container.
731
        :type overriding: :py:class:`DeclarativeContainer`
732

733
        :raise: :py:exc:`dependency_injector.errors.Error` if trying to
734
                override container by itself or its subclasses
735

736
        :rtype: None
737
        """
738
        if issubclass(cls, overriding):
1✔
739
            raise errors.Error("Container {0} could not be overridden "
1✔
740
                               "with itself or its subclasses".format(cls))
1✔
741

742
        cls.overridden += (overriding,)
1✔
743

744
        for name, provider in overriding.cls_providers.items():
1✔
745
            try:
1✔
746
                getattr(cls, name).override(provider)
1✔
747
            except AttributeError:
1✔
748
                pass
1✔
749

750
    @classmethod
1✔
751
    def reset_last_overriding(cls):
752
        """Reset last overriding provider for each container providers.
753

754
        :rtype: None
755
        """
756
        if not cls.overridden:
1✔
757
            raise errors.Error("Container {0} is not overridden".format(cls))
1✔
758

759
        cls.overridden = cls.overridden[:-1]
1✔
760

761
        for provider in cls.providers.values():
1✔
762
            provider.reset_last_overriding()
1✔
763

764
    @classmethod
1✔
765
    def reset_override(cls):
766
        """Reset all overridings for each container providers.
767

768
        :rtype: None
769
        """
770
        cls.overridden = tuple()
1✔
771

772
        for provider in cls.providers.values():
1✔
773
            provider.reset_override()
1✔
774

775

776
class SingletonResetContext:
1✔
777

778
    def __init__(self, container):
1✔
779
        self._container = container
1✔
780

781
    def __enter__(self):
1✔
782
        return self._container
1✔
783

784
    def __exit__(self, *_):
1✔
785
        self._container.reset_singletons()
1✔
786

787

788

789
class ProvidersOverridingContext:
1✔
790

791
    def __init__(self, container, overridden_providers):
1✔
792
        self._container = container
1✔
793
        self._overridden_providers = overridden_providers
1✔
794

795
    def __enter__(self):
1✔
796
        return self._container
1✔
797

798
    def __exit__(self, *_):
1✔
799
        for provider in self._overridden_providers:
1✔
800
            provider.reset_last_overriding()
1✔
801

802

803
def override(object container):
1✔
804
    """:py:class:`DeclarativeContainer` overriding decorator.
805

806
    :param container: Container that should be overridden by decorated
807
                      container.
808
    :type container: :py:class:`DeclarativeContainer`
809

810
    :return: Declarative container overriding decorator.
811
    :rtype: callable(:py:class:`DeclarativeContainer`)
812
    """
813
    def _decorator(object overriding_container):
1✔
814
        """Overriding decorator."""
815
        container.override(overriding_container)
1✔
816
        return overriding_container
1✔
817
    return _decorator
1✔
818

819

820
def copy(object base_container):
1✔
821
    """:py:class:`DeclarativeContainer` copying decorator.
822

823
    This decorator copies all providers from provided container to decorated one.
824
    If one of the decorated container providers matches to source container
825
    providers by name, it would be replaced by reference.
826

827
    :param base_container: Container that should be copied by decorated container.
828
    :type base_container: :py:class:`DeclarativeContainer`
829

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

841
            if hasattr(new_provider, "providers") and hasattr(source_provider, "providers"):
1✔
842
                sub_memo = _get_memo_for_matching_names(new_provider.providers, source_provider.providers)
1✔
843
                memo.update(sub_memo)
1✔
844
        return memo
1✔
845

846
    def _decorator(new_container):
1✔
847
        memo = {}
1✔
848
        memo.update(_get_memo_for_matching_names(new_container.cls_providers, base_container.providers))
1✔
849

850
        new_providers = {}
1✔
851
        new_providers.update(providers.deepcopy(base_container.providers, memo))
1✔
852
        new_providers.update(providers.deepcopy(new_container.cls_providers, memo))
1✔
853

854
        for name, provider in new_providers.items():
1✔
855
            setattr(new_container, name, provider)
1✔
856
        return new_container
1✔
857

858
    return _decorator
1✔
859

860

861
cpdef bint is_container(object instance):
1✔
862
    """Check if instance is container instance.
863

864
    :param instance: Instance to be checked.
865
    :type instance: object
866

867
    :rtype: bool
868
    """
869
    return getattr(instance, "__IS_CONTAINER__", False) is True
1✔
870

871

872
cpdef object _check_provider_type(object container, object provider):
1✔
873
    if not isinstance(provider, container.provider_type):
1✔
874
        raise errors.Error("{0} can contain only {1} "
1✔
875
                           "instances".format(container, container.provider_type))
1✔
876

877

878
cpdef bint _any_relative_string_imports_in(object modules):
1✔
879
    for module in modules:
1✔
880
        if not isinstance(module, str):
1✔
881
            continue
1✔
882
        if module.startswith("."):
1✔
883
            return True
1✔
884
    else:
885
        return False
1✔
886

887

888
cpdef list _resolve_string_imports(object modules, object from_package):
1✔
889
    return [
1✔
890
        importlib.import_module(module, from_package) if isinstance(module, str) else module
1✔
891
        for module in modules
1✔
892
    ]
893

894

895
cpdef object _resolve_calling_package_name():
1✔
896
    stack = inspect.stack()
1✔
897
    pre_last_frame = stack[0]
1✔
898
    module = inspect.getmodule(pre_last_frame[0])
1✔
899
    return module.__package__
1✔
900

901

902
cpdef object _resolve_package_name_from_cls(cls):
1✔
903
    module = importlib.import_module(cls.__module__)
1✔
904
    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