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

ets-labs / python-dependency-injector / 15679083919

16 Jun 2025 10:51AM UTC coverage: 94.59%. Remained the same
15679083919

push

github

ZipFile
Upgrade cibuildwheel

3392 of 3586 relevant lines covered (94.59%)

0.95 hits per line

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

98.86
/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, resource_type=providers.Resource):
1✔
319
        """Initialize all container resources."""
320

321
        if not issubclass(resource_type, providers.Resource):
1✔
322
            raise TypeError("resource_type must be a subclass of Resource provider")
1✔
323

324
        futures = []
1✔
325

326
        for provider in self.traverse(types=[resource_type]):
1✔
327
            resource = provider.init()
1✔
328

329
            if __is_future_or_coroutine(resource):
1✔
330
                futures.append(resource)
1✔
331

332
        if futures:
1✔
333
            return asyncio.gather(*futures)
1✔
334

335
    def shutdown_resources(self, resource_type=providers.Resource):
1✔
336
        """Shutdown all container resources."""
337

338
        if not issubclass(resource_type, providers.Resource):
1✔
339
            raise TypeError("resource_type must be a subclass of Resource provider")
1✔
340

341
        def _independent_resources(resources):
1✔
342
            for resource in resources:
1✔
343
                for other_resource in resources:
1✔
344
                    if not other_resource.initialized:
1✔
345
                        continue
1✔
346
                    if resource in other_resource.related:
1✔
347
                        break
1✔
348
                else:
349
                    yield resource
1✔
350

351
        async def _async_ordered_shutdown(resources):
1✔
352
            while any(resource.initialized for resource in resources):
1✔
353
                resources_to_shutdown = list(_independent_resources(resources))
1✔
354
                if not resources_to_shutdown:
1✔
355
                    raise RuntimeError("Unable to resolve resources shutdown order")
1✔
356
                futures = []
1✔
357
                for resource in resources_to_shutdown:
1✔
358
                    result = resource.shutdown()
1✔
359
                    if __is_future_or_coroutine(result):
1✔
360
                        futures.append(result)
1✔
361
                await asyncio.gather(*futures)
1✔
362

363
        def _sync_ordered_shutdown(resources):
1✔
364
            while any(resource.initialized for resource in resources):
1✔
365
                resources_to_shutdown = list(_independent_resources(resources))
1✔
366
                if not resources_to_shutdown:
1✔
367
                    raise RuntimeError("Unable to resolve resources shutdown order")
1✔
368
                for resource in resources_to_shutdown:
1✔
369
                    resource.shutdown()
1✔
370

371
        resources = list(self.traverse(types=[resource_type]))
1✔
372
        if any(resource.is_async_mode_enabled() for resource in resources):
1✔
373
            return _async_ordered_shutdown(resources)
1✔
374
        else:
375
            return _sync_ordered_shutdown(resources)
1✔
376

377
    def load_config(self):
1✔
378
        """Load configuration."""
379
        config: providers.Configuration
380
        for config in self.traverse(types=[providers.Configuration]):
1✔
381
            config.load()
1✔
382

383
    def apply_container_providers_overridings(self):
1✔
384
        """Apply container providers overridings."""
385
        for provider in self.traverse(types=[providers.Container]):
1✔
386
            provider.apply_overridings()
1✔
387

388
    def reset_singletons(self):
1✔
389
        """Reset container singletons."""
390
        for provider in self.traverse(types=[providers.BaseSingleton]):
1✔
391
            provider.reset()
1✔
392
        return SingletonResetContext(self)
1✔
393

394
    def check_dependencies(self):
1✔
395
        """Check if container dependencies are defined.
396

397
        If any dependency is undefined, raises an error.
398
        """
399
        undefined = [
1✔
400
            dependency
1✔
401
            for dependency in self.traverse(types=[providers.Dependency])
1✔
402
            if not dependency.is_defined
1✔
403
        ]
404

405
        if not undefined:
1✔
406
            return
1✔
407

408
        container_name = self.parent_name if self.parent_name else self.__class__.__name__
1✔
409
        undefined_names = [
1✔
410
            f"\"{dependency.parent_name if dependency.parent_name else dependency}\""
1✔
411
            for dependency in undefined
1✔
412
        ]
413
        raise errors.Error(
1✔
414
            f"Container \"{container_name}\" has undefined dependencies: "
1✔
415
            f"{', '.join(undefined_names)}",
1✔
416
        )
417

418
    def from_schema(self, schema):
1✔
419
        """Build container providers from schema."""
420
        from .schema import build_schema
1✔
421
        for name, provider in build_schema(schema).items():
1✔
422
            self.set_provider(name, provider)
1✔
423

424
    def from_yaml_schema(self, filepath, loader=None):
1✔
425
        """Build container providers from YAML schema.
426

427
        You can specify type of loader as a second argument. By default, method
428
        uses ``SafeLoader``.
429
        """
430
        if yaml is None:
1✔
431
            raise errors.Error(
1✔
432
                "Unable to load yaml schema - PyYAML is not installed. "
433
                "Install PyYAML or install Dependency Injector with yaml extras: "
434
                "\"pip install dependency-injector[yaml]\""
435
            )
436

437
        if loader is None:
1✔
438
            loader = yaml.SafeLoader
1✔
439

440
        with open(filepath) as file:
1✔
441
            schema = yaml.load(file, loader)
1✔
442

443
        self.from_schema(schema)
1✔
444

445
    def from_json_schema(self, filepath):
1✔
446
        """Build container providers from JSON schema."""
447
        with open(filepath) as file:
1✔
448
            schema = json.load(file)
1✔
449
        self.from_schema(schema)
1✔
450

451
    def resolve_provider_name(self, provider):
1✔
452
        """Try to resolve provider name."""
453
        for provider_name, container_provider in self.providers.items():
1✔
454
            if container_provider is provider:
1✔
455
                return provider_name
1✔
456
        else:
457
            raise errors.Error(f"Can not resolve name for provider \"{provider}\"")
1✔
458

459
    @property
1✔
460
    def parent_name(self):
461
        """Return parent name."""
462
        if self.parent:
1✔
463
            return self.parent.parent_name
1✔
464

465
        if self.declarative_parent:
1✔
466
            return self.declarative_parent.__name__
1✔
467

468
        return None
1✔
469

470
    def assign_parent(self, parent):
1✔
471
        """Assign parent."""
472
        self.parent = parent
1✔
473

474

475
class DeclarativeContainerMetaClass(type):
1✔
476
    """Declarative inversion of control container meta class."""
477

478
    def __new__(type mcs, str class_name, tuple bases, dict attributes):
1✔
479
        """Declarative container class factory."""
480
        self = mcs.__fetch_self(attributes)
1✔
481
        if self is None:
1✔
482
            self = providers.Self()
1✔
483

484
        containers = {
1✔
485
            name: container
1✔
486
            for name, container in attributes.items()
1✔
487
            if is_container(container)
1✔
488
        }
489

490
        cls_providers = {
1✔
491
            name: provider
1✔
492
            for name, provider in attributes.items()
1✔
493
            if isinstance(provider, providers.Provider) and not isinstance(provider, providers.Self)
1✔
494
        }
495

496
        inherited_providers = {
1✔
497
            name: provider
1✔
498
            for base in bases
1✔
499
            if is_container(base) and base is not DynamicContainer
1✔
500
            for name, provider in base.providers.items()
1✔
501
        }
502

503
        all_providers = {}
1✔
504
        all_providers.update(inherited_providers)
1✔
505
        all_providers.update(cls_providers)
1✔
506

507
        wiring_config = attributes.get("wiring_config")
1✔
508
        if wiring_config is None:
1✔
509
            wiring_config = WiringConfiguration()
1✔
510
        if wiring_config is not None and not isinstance(wiring_config, WiringConfiguration):
1✔
511
            raise errors.Error(
×
512
                "Wiring configuration should be an instance of WiringConfiguration, "
513
                "instead got {0}".format(wiring_config)
×
514
            )
515

516
        attributes["containers"] = containers
1✔
517
        attributes["inherited_providers"] = inherited_providers
1✔
518
        attributes["cls_providers"] = cls_providers
1✔
519
        attributes["providers"] = all_providers
1✔
520
        attributes["wiring_config"] = wiring_config
1✔
521

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

524
        self.set_container(cls)
1✔
525
        cls.__self__ = self
1✔
526

527
        for provider in cls.providers.values():
1✔
528
            _check_provider_type(cls, provider)
1✔
529

530
        for provider in cls.cls_providers.values():
1✔
531
            if isinstance(provider, providers.CHILD_PROVIDERS):
1✔
532
                provider.assign_parent(cls)
1✔
533

534
        return cls
1✔
535

536
    def __setattr__(cls, name, value):
1✔
537
        """Set class attribute.
538

539
        If value of attribute is provider, it will be added into providers
540
        dictionary.
541

542
        :param name: Attribute name
543
        :type name: object
544

545
        :param value: Attribute value
546
        :type value: object
547

548
        :rtype: None
549
        """
550
        if isinstance(value, providers.Provider) and name != "__self__":
1✔
551
            _check_provider_type(cls, value)
1✔
552

553
            if isinstance(value, providers.CHILD_PROVIDERS):
1✔
554
                value.assign_parent(cls)
1✔
555

556
            cls.providers[name] = value
1✔
557
            cls.cls_providers[name] = value
1✔
558
        super(DeclarativeContainerMetaClass, cls).__setattr__(name, value)
1✔
559

560
    def __delattr__(cls, name):
1✔
561
        """Delete class attribute.
562

563
        If value of attribute is provider, it will be deleted from providers
564
        dictionary.
565

566
        :param name: Attribute name
567
        :type name: object
568

569
        :rtype: None
570
        """
571
        if name in cls.providers and name in cls.cls_providers:
1✔
572
            del cls.providers[name]
1✔
573
            del cls.cls_providers[name]
1✔
574
        super(DeclarativeContainerMetaClass, cls).__delattr__(name)
1✔
575

576
    @property
1✔
577
    def dependencies(cls):
578
        """Return dependency providers dictionary.
579

580
        Dependency providers can be both of :py:class:`dependency_injector.providers.Dependency` and
581
        :py:class:`dependency_injector.providers.DependenciesContainer`.
582

583
        :rtype:
584
            dict[str, :py:class:`dependency_injector.providers.Provider`]
585
        """
586
        return {
1✔
587
            name: provider
1✔
588
            for name, provider in cls.providers.items()
1✔
589
            if isinstance(provider, (providers.Dependency, providers.DependenciesContainer))
1✔
590
        }
591

592
    def traverse(cls, types=None):
1✔
593
        """Return providers traversal generator."""
594
        yield from providers.traverse(*cls.providers.values(), types=types)
1✔
595

596
    def resolve_provider_name(cls, provider):
1✔
597
        """Try to resolve provider name."""
598
        for provider_name, container_provider in cls.providers.items():
1✔
599
            if container_provider is provider:
1✔
600
                return provider_name
1✔
601
        else:
602
            raise errors.Error(f"Can not resolve name for provider \"{provider}\"")
1✔
603

604
    @property
1✔
605
    def parent_name(cls):
606
        """Return parent name."""
607
        return cls.__name__
1✔
608

609
    @staticmethod
1✔
610
    def __fetch_self(attributes):
611
        self = None
1✔
612
        alt_names = []
1✔
613

614
        for name, value in attributes.items():
1✔
615
            if not isinstance(value, providers.Self):
1✔
616
                continue
1✔
617

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

621
            if name != "__self__":
1✔
622
                alt_names.append(name)
1✔
623

624
            self = value
1✔
625

626
        if self:
1✔
627
            self.set_alt_names(alt_names)
1✔
628

629
        return self
1✔
630

631

632
class DeclarativeContainer(Container, metaclass=DeclarativeContainerMetaClass):
1✔
633
    """Declarative inversion of control container.
634

635
    .. code-block:: python
636

637
        class Services(DeclarativeContainer):
638
            auth = providers.Factory(AuthService)
639
            users = providers.Factory(UsersService,
640
                                      auth_service=auth)
641
    """
642

643
    __IS_CONTAINER__ = True
1✔
644

645
    provider_type = providers.Provider
1✔
646
    """Type of providers that could be placed in container.
647

648
    :type: type
649
    """
650

651
    instance_type = DynamicContainer
1✔
652
    """Type of container that is returned on instantiating declarative
653
    container.
654

655
    :type: type
656
    """
657

658
    containers = dict()
1✔
659
    """Read-only dictionary of all nested containers.
660

661
    :type: dict[str, :py:class:`DeclarativeContainer`]
662
    """
663

664
    providers = dict()
1✔
665
    """Read-only dictionary of all providers.
666

667
    :type: dict[str, :py:class:`dependency_injector.providers.Provider`]
668
    """
669

670
    wiring_config = WiringConfiguration()
1✔
671
    """Wiring configuration.
672

673
    :type: WiringConfiguration
674
    """
675

676
    auto_load_config = True
1✔
677
    """Automatically load configuration when the container is created.
678

679
    :type: bool
680
    """
681

682
    cls_providers = dict()
1✔
683
    """Read-only dictionary of current container providers.
684

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

688
    inherited_providers = dict()
1✔
689
    """Read-only dictionary of inherited providers.
690

691
    :type: dict[str, :py:class:`dependency_injector.providers.Provider`]
692
    """
693

694
    overridden = tuple()
1✔
695
    """Tuple of overriding containers.
696

697
    :type: tuple[:py:class:`DeclarativeContainer`]
698
    """
699

700
    __self__ = None
1✔
701
    """Provider that provides current container.
702

703
    :type: :py:class:`dependency_injector.providers.Provider`
704
    """
705

706
    def __new__(cls, **overriding_providers):
1✔
707
        """Constructor.
708

709
        :return: Dynamic container with copy of all providers.
710
        :rtype: :py:class:`DynamicContainer`
711
        """
712
        container = cls.instance_type()
1✔
713
        container.provider_type = cls.provider_type
1✔
714
        container.wiring_config = copy_module.deepcopy(cls.wiring_config)
1✔
715
        container.declarative_parent = cls
1✔
716

717
        copied_providers = providers.deepcopy({ **cls.providers, **{"@@self@@": cls.__self__}})
1✔
718
        copied_self = copied_providers.pop("@@self@@")
1✔
719
        copied_self.set_container(container)
1✔
720

721
        container.__self__ = copied_self
1✔
722
        for name in copied_self.alt_names:
1✔
723
            container.set_provider(name, copied_self)
1✔
724

725
        for name, provider in copied_providers.items():
1✔
726
            container.set_provider(name, provider)
1✔
727

728
        if cls.auto_load_config:
1✔
729
            container.load_config()
1✔
730

731
        container.override_providers(**overriding_providers)
1✔
732
        container.apply_container_providers_overridings()
1✔
733

734
        if container.is_auto_wiring_enabled():
1✔
735
            container.wire()
1✔
736

737
        return container
1✔
738

739
    @classmethod
1✔
740
    def override(cls, object overriding):
741
        """Override current container by overriding container.
742

743
        :param overriding: Overriding container.
744
        :type overriding: :py:class:`DeclarativeContainer`
745

746
        :raise: :py:exc:`dependency_injector.errors.Error` if trying to
747
                override container by itself or its subclasses
748

749
        :rtype: None
750
        """
751
        if issubclass(cls, overriding):
1✔
752
            raise errors.Error("Container {0} could not be overridden "
1✔
753
                               "with itself or its subclasses".format(cls))
1✔
754

755
        cls.overridden += (overriding,)
1✔
756

757
        for name, provider in overriding.cls_providers.items():
1✔
758
            try:
1✔
759
                getattr(cls, name).override(provider)
1✔
760
            except AttributeError:
1✔
761
                pass
1✔
762

763
    @classmethod
1✔
764
    def reset_last_overriding(cls):
765
        """Reset last overriding provider for each container providers.
766

767
        :rtype: None
768
        """
769
        if not cls.overridden:
1✔
770
            raise errors.Error("Container {0} is not overridden".format(cls))
1✔
771

772
        cls.overridden = cls.overridden[:-1]
1✔
773

774
        for provider in cls.providers.values():
1✔
775
            provider.reset_last_overriding()
1✔
776

777
    @classmethod
1✔
778
    def reset_override(cls):
779
        """Reset all overridings for each container providers.
780

781
        :rtype: None
782
        """
783
        cls.overridden = tuple()
1✔
784

785
        for provider in cls.providers.values():
1✔
786
            provider.reset_override()
1✔
787

788

789
class SingletonResetContext:
1✔
790

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

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

797
    def __exit__(self, *_):
1✔
798
        self._container.reset_singletons()
1✔
799

800

801

802
class ProvidersOverridingContext:
1✔
803

804
    def __init__(self, container, overridden_providers):
1✔
805
        self._container = container
1✔
806
        self._overridden_providers = overridden_providers
1✔
807

808
    def __enter__(self):
1✔
809
        return self._container
1✔
810

811
    def __exit__(self, *_):
1✔
812
        for provider in self._overridden_providers:
1✔
813
            provider.reset_last_overriding()
1✔
814

815

816
def override(object container):
1✔
817
    """:py:class:`DeclarativeContainer` overriding decorator.
818

819
    :param container: Container that should be overridden by decorated
820
                      container.
821
    :type container: :py:class:`DeclarativeContainer`
822

823
    :return: Declarative container overriding decorator.
824
    :rtype: callable(:py:class:`DeclarativeContainer`)
825
    """
826
    def _decorator(object overriding_container):
1✔
827
        """Overriding decorator."""
828
        container.override(overriding_container)
1✔
829
        return overriding_container
1✔
830
    return _decorator
1✔
831

832

833
def copy(object base_container):
1✔
834
    """:py:class:`DeclarativeContainer` copying decorator.
835

836
    This decorator copies all providers from provided container to decorated one.
837
    If one of the decorated container providers matches to source container
838
    providers by name, it would be replaced by reference.
839

840
    :param base_container: Container that should be copied by decorated container.
841
    :type base_container: :py:class:`DeclarativeContainer`
842

843
    :return: Declarative container copying decorator.
844
    :rtype: callable(:py:class:`DeclarativeContainer`)
845
    """
846
    def _get_memo_for_matching_names(new_providers, base_providers):
1✔
847
        memo = {}
1✔
848
        for new_provider_name, new_provider in new_providers.items():
1✔
849
            if new_provider_name not in base_providers:
1✔
850
                continue
1✔
851
            source_provider = base_providers[new_provider_name]
1✔
852
            memo[id(source_provider)] = new_provider
1✔
853

854
            if hasattr(new_provider, "providers") and hasattr(source_provider, "providers"):
1✔
855
                sub_memo = _get_memo_for_matching_names(new_provider.providers, source_provider.providers)
1✔
856
                memo.update(sub_memo)
1✔
857
        return memo
1✔
858

859
    def _decorator(new_container):
1✔
860
        memo = {}
1✔
861
        memo.update(_get_memo_for_matching_names(new_container.cls_providers, base_container.providers))
1✔
862

863
        new_providers = {}
1✔
864
        new_providers.update(providers.deepcopy(base_container.providers, memo))
1✔
865
        new_providers.update(providers.deepcopy(new_container.cls_providers, memo))
1✔
866

867
        for name, provider in new_providers.items():
1✔
868
            setattr(new_container, name, provider)
1✔
869
        return new_container
1✔
870

871
    return _decorator
1✔
872

873

874
cpdef bint is_container(object instance):
1✔
875
    """Check if instance is container instance.
876

877
    :param instance: Instance to be checked.
878
    :type instance: object
879

880
    :rtype: bool
881
    """
882
    return getattr(instance, "__IS_CONTAINER__", False) is True
1✔
883

884

885
cpdef object _check_provider_type(object container, object provider):
1✔
886
    if not isinstance(provider, container.provider_type):
1✔
887
        raise errors.Error("{0} can contain only {1} "
1✔
888
                           "instances".format(container, container.provider_type))
1✔
889

890

891
cpdef bint _any_relative_string_imports_in(object modules):
1✔
892
    for module in modules:
1✔
893
        if not isinstance(module, str):
1✔
894
            continue
1✔
895
        if module.startswith("."):
1✔
896
            return True
1✔
897
    else:
898
        return False
1✔
899

900

901
cpdef list _resolve_string_imports(object modules, object from_package):
1✔
902
    return [
1✔
903
        importlib.import_module(module, from_package) if isinstance(module, str) else module
1✔
904
        for module in modules
1✔
905
    ]
906

907

908
cpdef object _resolve_calling_package_name():
1✔
909
    stack = inspect.stack()
1✔
910
    pre_last_frame = stack[0]
1✔
911
    module = inspect.getmodule(pre_last_frame[0])
1✔
912
    return module.__package__
1✔
913

914

915
cpdef object _resolve_package_name_from_cls(cls):
1✔
916
    module = importlib.import_module(cls.__module__)
1✔
917
    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