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

ets-labs / python-dependency-injector / 13524441259

23 Feb 2025 05:22PM UTC coverage: 93.883%. Remained the same
13524441259

push

github

ZipFile
Bump version to v4.46.0

3361 of 3580 relevant lines covered (93.88%)

0.94 hits per line

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

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

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

11
try:
1✔
12
    import asyncio
1✔
13
except ImportError:
×
14
    asyncio = None
×
15

16
try:
1✔
17
    import yaml
1✔
18
except ImportError:
×
19
    yaml = None
×
20

21
from . import providers, errors
1✔
22
from .providers cimport __is_future_or_coroutine
23

24

25
if sys.version_info[:2] >= (3, 6):
1✔
26
    from .wiring import wire, unwire
1✔
27
else:
28
    def wire(*args, **kwargs):
×
29
        raise NotImplementedError("Wiring requires Python 3.6 or above")
×
30

31
    def unwire(*args, **kwargs):
×
32
        raise NotImplementedError("Wiring requires Python 3.6 or above")
×
33

34
if sys.version_info[:2] == (3, 5):
1✔
35
    warnings.warn(
×
36
        "Dependency Injector will drop support of Python 3.5 after Jan 1st of 2022. "
37
        "This does not mean that there will be any immediate breaking changes, "
38
        "but tests will no longer be executed on Python 3.5, and bugs will not be addressed.",
39
        category=DeprecationWarning,
×
40
    )
41

42

43
class WiringConfiguration:
1✔
44
    """Container wiring configuration."""
45

46
    def __init__(self, modules=None, packages=None, from_package=None, auto_wire=True):
1✔
47
        self.modules = [*modules] if modules else []
1✔
48
        self.packages = [*packages] if packages else []
1✔
49
        self.from_package = from_package
1✔
50
        self.auto_wire = auto_wire
1✔
51

52
    def __deepcopy__(self, memo=None):
1✔
53
        return self.__class__(self.modules, self.packages, self.from_package, self.auto_wire)
1✔
54

55

56
class Container(object):
1✔
57
    """Abstract container."""
58

59

60
class DynamicContainer(Container):
1✔
61
    """Dynamic inversion of control container.
62

63
    .. code-block:: python
64

65
        services = DynamicContainer()
66
        services.auth = providers.Factory(AuthService)
67
        services.users = providers.Factory(UsersService,
68
                                           auth_service=services.auth)
69

70
    .. py:attribute:: providers
71

72
        Read-only dictionary of all providers.
73

74
        :type: dict[str, :py:class:`dependency_injector.providers.Provider`]
75

76
    .. py:attribute:: overridden
77

78
        Tuple of overriding containers.
79

80
        :type: tuple[:py:class:`DynamicContainer`]
81

82
    .. py:attribute:: provider_type
83

84
        Type of providers that could be placed in container.
85

86
        :type: type
87
    """
88

89
    __IS_CONTAINER__ = True
1✔
90

91
    def __init__(self):
1✔
92
        """Initializer.
93

94
        :rtype: None
95
        """
96
        self.provider_type = providers.Provider
1✔
97
        self.providers = {}
1✔
98
        self.overridden = tuple()
1✔
99
        self.parent = None
1✔
100
        self.declarative_parent = None
1✔
101
        self.wiring_config = WiringConfiguration()
1✔
102
        self.wired_to_modules = []
1✔
103
        self.wired_to_packages = []
1✔
104
        self.__self__ = providers.Self(self)
1✔
105
        super(DynamicContainer, self).__init__()
1✔
106

107
    def __deepcopy__(self, memo):
1✔
108
        """Create and return full copy of container."""
109
        copied = memo.get(id(self))
1✔
110
        if copied is not None:
1✔
111
            return copied
×
112

113
        copied = self.__class__()
1✔
114
        memo[id(self)] = copied
1✔
115

116
        copied.__self__ = providers.deepcopy(self.__self__, memo)
1✔
117
        for name in copied.__self__.alt_names:
1✔
118
            copied.set_provider(name, copied.__self__)
1✔
119

120
        copied.provider_type = providers.Provider
1✔
121
        copied.overridden = providers.deepcopy(self.overridden, memo)
1✔
122
        copied.wiring_config = copy_module.deepcopy(self.wiring_config, memo)
1✔
123
        copied.declarative_parent = self.declarative_parent
1✔
124

125
        for name, provider in providers.deepcopy(self.providers, memo).items():
1✔
126
            copied.set_provider(name, provider)
1✔
127

128
        copied.parent = providers.deepcopy(self.parent, memo)
1✔
129

130
        return copied
1✔
131

132
    def __setattr__(self, name, value):
1✔
133
        """Set instance attribute.
134

135
        If value of attribute is provider, it will be added into providers
136
        dictionary.
137

138
        :param name: Attribute name
139
        :type name: object
140

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

144
        :rtype: None
145
        """
146
        if isinstance(value, providers.Provider) \
1✔
147
                and not isinstance(value, providers.Self) \
1✔
148
                and name != "parent":
1✔
149
            _check_provider_type(self, value)
1✔
150

151
            self.providers[name] = value
1✔
152

153
            if isinstance(value, providers.CHILD_PROVIDERS):
1✔
154
                value.assign_parent(self)
1✔
155

156
        super(DynamicContainer, self).__setattr__(name, value)
1✔
157

158
    def __delattr__(self, name):
1✔
159
        """Delete instance attribute.
160

161
        If value of attribute is provider, it will be deleted from providers
162
        dictionary.
163

164
        :param name: Attribute name
165
        :type name: object
166

167
        :rtype: None
168
        """
169
        if name in self.providers:
1✔
170
            del self.providers[name]
1✔
171
        super(DynamicContainer, self).__delattr__(name)
1✔
172

173
    @property
1✔
174
    def dependencies(self):
175
        """Return dependency providers dictionary.
176

177
        Dependency providers can be both of :py:class:`dependency_injector.providers.Dependency` and
178
        :py:class:`dependency_injector.providers.DependenciesContainer`.
179

180
        :rtype:
181
            dict[str, :py:class:`dependency_injector.providers.Provider`]
182
        """
183
        return {
1✔
184
            name: provider
1✔
185
            for name, provider in self.providers.items()
1✔
186
            if isinstance(provider, (providers.Dependency, providers.DependenciesContainer))
1✔
187
        }
188

189
    def traverse(self, types=None):
1✔
190
        """Return providers traversal generator."""
191
        yield from providers.traverse(*self.providers.values(), types=types)
1✔
192

193
    def set_providers(self, **providers):
1✔
194
        """Set container providers.
195

196
        :param providers: Dictionary of providers
197
        :type providers:
198
            dict[object, :py:class:`dependency_injector.providers.Provider`]
199

200
        :rtype: None
201
        """
202
        for name, provider in providers.items():
1✔
203
            setattr(self, name, provider)
1✔
204

205
    def set_provider(self, name, provider):
1✔
206
        """Set container provider.
207

208
        :param name: Provider name
209
        :type name: str
210

211
        :param provider: Provider
212
        :type provider: :py:class:`dependency_injector.providers.Provider`
213

214
        :rtype: None
215
        """
216
        setattr(self, name, provider)
1✔
217

218
    def override(self, object overriding):
1✔
219
        """Override current container by overriding container.
220

221
        :param overriding: Overriding container.
222
        :type overriding: :py:class:`DynamicContainer`
223

224
        :raise: :py:exc:`dependency_injector.errors.Error` if trying to
225
                override container by itself
226

227
        :rtype: None
228
        """
229
        if overriding is self:
1✔
230
            raise errors.Error("Container {0} could not be overridden "
1✔
231
                               "with itself".format(self))
1✔
232

233
        self.overridden += (overriding,)
1✔
234

235
        for name, provider in overriding.providers.items():
1✔
236
            try:
1✔
237
                getattr(self, name).override(provider)
1✔
238
            except AttributeError:
1✔
239
                pass
240

241
    def override_providers(self, **overriding_providers):
1✔
242
        """Override container providers.
243

244
        :param overriding_providers: Dictionary of providers
245
        :type overriding_providers:
246
            dict[str, :py:class:`dependency_injector.providers.Provider`]
247

248
        :rtype: None
249
        """
250
        overridden_providers = []
1✔
251
        for name, overriding_provider in overriding_providers.items():
1✔
252
            container_provider = getattr(self, name)
1✔
253
            container_provider.override(overriding_provider)
1✔
254
            overridden_providers.append(container_provider)
1✔
255
        return ProvidersOverridingContext(self, overridden_providers)
1✔
256

257
    def reset_last_overriding(self):
1✔
258
        """Reset last overriding provider for each container providers.
259

260
        :rtype: None
261
        """
262
        if not self.overridden:
1✔
263
            raise errors.Error("Container {0} is not overridden".format(self))
1✔
264

265
        self.overridden = self.overridden[:-1]
1✔
266

267
        for provider in self.providers.values():
1✔
268
            provider.reset_last_overriding()
1✔
269

270
    def reset_override(self):
1✔
271
        """Reset all overridings for each container providers.
272

273
        :rtype: None
274
        """
275
        self.overridden = tuple()
1✔
276

277
        for provider in self.providers.values():
1✔
278
            provider.reset_override()
1✔
279

280
    def is_auto_wiring_enabled(self):
1✔
281
        """Check if auto wiring is needed."""
282
        return self.wiring_config.auto_wire is True
1✔
283

284
    def wire(self, modules=None, packages=None, from_package=None):
1✔
285
        """Wire container providers with provided packages and modules.
286

287
        :rtype: None
288
        """
289
        if modules is None and self.wiring_config.modules:
1✔
290
            modules = self.wiring_config.modules
1✔
291
        if packages is None and self.wiring_config.packages:
1✔
292
            packages = self.wiring_config.packages
1✔
293

294
        modules = [*modules] if modules else []
1✔
295
        packages = [*packages] if packages else []
1✔
296

297
        if _any_relative_string_imports_in(modules) or _any_relative_string_imports_in(packages):
1✔
298
            if from_package is None:
1✔
299
                if self.wiring_config.from_package is not None:
1✔
300
                    from_package = self.wiring_config.from_package
1✔
301
                elif self.declarative_parent is not None \
1✔
302
                        and (self.wiring_config.modules or self.wiring_config.packages):
1✔
303
                    with contextlib.suppress(Exception):
1✔
304
                        from_package = _resolve_package_name_from_cls(self.declarative_parent)
1✔
305
                else:
306
                    with contextlib.suppress(Exception):
1✔
307
                        from_package = _resolve_calling_package_name()
1✔
308

309
        modules = _resolve_string_imports(modules, from_package)
1✔
310
        packages = _resolve_string_imports(packages, from_package)
1✔
311

312
        if not modules and not packages:
1✔
313
            return
1✔
314

315
        wire(
1✔
316
            container=self,
1✔
317
            modules=modules,
1✔
318
            packages=packages,
1✔
319
        )
320

321
        if modules:
1✔
322
            self.wired_to_modules.extend(modules)
1✔
323
        if packages:
1✔
324
            self.wired_to_packages.extend(packages)
1✔
325

326
    def unwire(self):
1✔
327
        """Unwire container providers from previously wired packages and modules."""
328
        unwire(
1✔
329
            modules=self.wired_to_modules,
1✔
330
            packages=self.wired_to_packages,
1✔
331
        )
332

333
        self.wired_to_modules.clear()
1✔
334
        self.wired_to_packages.clear()
1✔
335

336
    def init_resources(self):
1✔
337
        """Initialize all container resources."""
338
        futures = []
1✔
339

340
        for provider in self.traverse(types=[providers.Resource]):
1✔
341
            resource = provider.init()
1✔
342

343
            if __is_future_or_coroutine(resource):
1✔
344
                futures.append(resource)
1✔
345

346
        if futures:
1✔
347
            return asyncio.gather(*futures)
1✔
348

349
    def shutdown_resources(self):
1✔
350
        """Shutdown all container resources."""
351
        def _independent_resources(resources):
1✔
352
            for resource in resources:
1✔
353
                for other_resource in resources:
1✔
354
                    if not other_resource.initialized:
1✔
355
                        continue
1✔
356
                    if resource in other_resource.related:
1✔
357
                        break
1✔
358
                else:
359
                    yield resource
1✔
360

361
        async def _async_ordered_shutdown(resources):
1✔
362
            while any(resource.initialized for resource in resources):
1✔
363
                resources_to_shutdown = list(_independent_resources(resources))
1✔
364
                if not resources_to_shutdown:
1✔
365
                    raise RuntimeError("Unable to resolve resources shutdown order")
1✔
366
                futures = []
1✔
367
                for resource in resources_to_shutdown:
1✔
368
                    result = resource.shutdown()
1✔
369
                    if __is_future_or_coroutine(result):
1✔
370
                        futures.append(result)
1✔
371
                await asyncio.gather(*futures)
1✔
372

373
        def _sync_ordered_shutdown(resources):
1✔
374
            while any(resource.initialized for resource in resources):
1✔
375
                resources_to_shutdown = list(_independent_resources(resources))
1✔
376
                if not resources_to_shutdown:
1✔
377
                    raise RuntimeError("Unable to resolve resources shutdown order")
1✔
378
                for resource in resources_to_shutdown:
1✔
379
                    resource.shutdown()
1✔
380

381
        resources = list(self.traverse(types=[providers.Resource]))
1✔
382
        if any(resource.is_async_mode_enabled() for resource in resources):
1✔
383
            return _async_ordered_shutdown(resources)
1✔
384
        else:
385
            return _sync_ordered_shutdown(resources)
1✔
386

387
    def load_config(self):
1✔
388
        """Load configuration."""
389
        config: providers.Configuration
390
        for config in self.traverse(types=[providers.Configuration]):
1✔
391
            config.load()
1✔
392

393
    def apply_container_providers_overridings(self):
1✔
394
        """Apply container providers overridings."""
395
        for provider in self.traverse(types=[providers.Container]):
1✔
396
            provider.apply_overridings()
1✔
397

398
    def reset_singletons(self):
1✔
399
        """Reset container singletons."""
400
        for provider in self.traverse(types=[providers.BaseSingleton]):
1✔
401
            provider.reset()
1✔
402
        return SingletonResetContext(self)
1✔
403

404
    def check_dependencies(self):
1✔
405
        """Check if container dependencies are defined.
406

407
        If any dependency is undefined, raises an error.
408
        """
409
        undefined = [
1✔
410
            dependency
1✔
411
            for dependency in self.traverse(types=[providers.Dependency])
1✔
412
            if not dependency.is_defined
1✔
413
        ]
414

415
        if not undefined:
1✔
416
            return
1✔
417

418
        container_name = self.parent_name if self.parent_name else self.__class__.__name__
1✔
419
        undefined_names = [
1✔
420
            f"\"{dependency.parent_name if dependency.parent_name else dependency}\""
1✔
421
            for dependency in undefined
1✔
422
        ]
423
        raise errors.Error(
1✔
424
            f"Container \"{container_name}\" has undefined dependencies: "
1✔
425
            f"{', '.join(undefined_names)}",
1✔
426
        )
427

428
    def from_schema(self, schema):
1✔
429
        """Build container providers from schema."""
430
        from .schema import build_schema
1✔
431
        for name, provider in build_schema(schema).items():
1✔
432
            self.set_provider(name, provider)
1✔
433

434
    def from_yaml_schema(self, filepath, loader=None):
1✔
435
        """Build container providers from YAML schema.
436

437
        You can specify type of loader as a second argument. By default, method
438
        uses ``SafeLoader``.
439
        """
440
        if yaml is None:
1✔
441
            raise errors.Error(
1✔
442
                "Unable to load yaml schema - PyYAML is not installed. "
443
                "Install PyYAML or install Dependency Injector with yaml extras: "
444
                "\"pip install dependency-injector[yaml]\""
445
            )
446

447
        if loader is None:
1✔
448
            loader = yaml.SafeLoader
1✔
449

450
        with open(filepath) as file:
1✔
451
            schema = yaml.load(file, loader)
1✔
452

453
        self.from_schema(schema)
1✔
454

455
    def from_json_schema(self, filepath):
1✔
456
        """Build container providers from JSON schema."""
457
        with open(filepath) as file:
1✔
458
            schema = json.load(file)
1✔
459
        self.from_schema(schema)
1✔
460

461
    def resolve_provider_name(self, provider):
1✔
462
        """Try to resolve provider name."""
463
        for provider_name, container_provider in self.providers.items():
1✔
464
            if container_provider is provider:
1✔
465
                return provider_name
1✔
466
        else:
467
            raise errors.Error(f"Can not resolve name for provider \"{provider}\"")
1✔
468

469
    @property
1✔
470
    def parent_name(self):
471
        """Return parent name."""
472
        if self.parent:
1✔
473
            return self.parent.parent_name
1✔
474

475
        if self.declarative_parent:
1✔
476
            return self.declarative_parent.__name__
1✔
477

478
        return None
1✔
479

480
    def assign_parent(self, parent):
1✔
481
        """Assign parent."""
482
        self.parent = parent
1✔
483

484

485
class DeclarativeContainerMetaClass(type):
1✔
486
    """Declarative inversion of control container meta class."""
487

488
    def __new__(type mcs, str class_name, tuple bases, dict attributes):
1✔
489
        """Declarative container class factory."""
490
        self = mcs.__fetch_self(attributes)
1✔
491
        if self is None:
1✔
492
            self = providers.Self()
1✔
493

494
        containers = {
1✔
495
            name: container
1✔
496
            for name, container in attributes.items()
1✔
497
            if is_container(container)
1✔
498
        }
499

500
        cls_providers = {
1✔
501
            name: provider
1✔
502
            for name, provider in attributes.items()
1✔
503
            if isinstance(provider, providers.Provider) and not isinstance(provider, providers.Self)
1✔
504
        }
505

506
        inherited_providers = {
1✔
507
            name: provider
1✔
508
            for base in bases
1✔
509
            if is_container(base) and base is not DynamicContainer
1✔
510
            for name, provider in base.providers.items()
1✔
511
        }
512

513
        all_providers = {}
1✔
514
        all_providers.update(inherited_providers)
1✔
515
        all_providers.update(cls_providers)
1✔
516

517
        wiring_config = attributes.get("wiring_config")
1✔
518
        if wiring_config is None:
1✔
519
            wiring_config = WiringConfiguration()
1✔
520
        if wiring_config is not None and not isinstance(wiring_config, WiringConfiguration):
1✔
521
            raise errors.Error(
×
522
                "Wiring configuration should be an instance of WiringConfiguration, "
523
                "instead got {0}".format(wiring_config)
×
524
            )
525

526
        attributes["containers"] = containers
1✔
527
        attributes["inherited_providers"] = inherited_providers
1✔
528
        attributes["cls_providers"] = cls_providers
1✔
529
        attributes["providers"] = all_providers
1✔
530
        attributes["wiring_config"] = wiring_config
1✔
531

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

534
        self.set_container(cls)
1✔
535
        cls.__self__ = self
1✔
536

537
        for provider in cls.providers.values():
1✔
538
            _check_provider_type(cls, provider)
1✔
539

540
        for provider in cls.cls_providers.values():
1✔
541
            if isinstance(provider, providers.CHILD_PROVIDERS):
1✔
542
                provider.assign_parent(cls)
1✔
543

544
        return cls
1✔
545

546
    def __setattr__(cls, name, value):
1✔
547
        """Set class attribute.
548

549
        If value of attribute is provider, it will be added into providers
550
        dictionary.
551

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

555
        :param value: Attribute value
556
        :type value: object
557

558
        :rtype: None
559
        """
560
        if isinstance(value, providers.Provider) and name != "__self__":
1✔
561
            _check_provider_type(cls, value)
1✔
562

563
            if isinstance(value, providers.CHILD_PROVIDERS):
1✔
564
                value.assign_parent(cls)
1✔
565

566
            cls.providers[name] = value
1✔
567
            cls.cls_providers[name] = value
1✔
568
        super(DeclarativeContainerMetaClass, cls).__setattr__(name, value)
1✔
569

570
    def __delattr__(cls, name):
1✔
571
        """Delete class attribute.
572

573
        If value of attribute is provider, it will be deleted from providers
574
        dictionary.
575

576
        :param name: Attribute name
577
        :type name: object
578

579
        :rtype: None
580
        """
581
        if name in cls.providers and name in cls.cls_providers:
1✔
582
            del cls.providers[name]
1✔
583
            del cls.cls_providers[name]
1✔
584
        super(DeclarativeContainerMetaClass, cls).__delattr__(name)
1✔
585

586
    @property
1✔
587
    def dependencies(cls):
588
        """Return dependency providers dictionary.
589

590
        Dependency providers can be both of :py:class:`dependency_injector.providers.Dependency` and
591
        :py:class:`dependency_injector.providers.DependenciesContainer`.
592

593
        :rtype:
594
            dict[str, :py:class:`dependency_injector.providers.Provider`]
595
        """
596
        return {
1✔
597
            name: provider
1✔
598
            for name, provider in cls.providers.items()
1✔
599
            if isinstance(provider, (providers.Dependency, providers.DependenciesContainer))
1✔
600
        }
601

602
    def traverse(cls, types=None):
1✔
603
        """Return providers traversal generator."""
604
        yield from providers.traverse(*cls.providers.values(), types=types)
1✔
605

606
    def resolve_provider_name(cls, provider):
1✔
607
        """Try to resolve provider name."""
608
        for provider_name, container_provider in cls.providers.items():
1✔
609
            if container_provider is provider:
1✔
610
                return provider_name
1✔
611
        else:
612
            raise errors.Error(f"Can not resolve name for provider \"{provider}\"")
1✔
613

614
    @property
1✔
615
    def parent_name(cls):
616
        """Return parent name."""
617
        return cls.__name__
1✔
618

619
    @staticmethod
1✔
620
    def __fetch_self(attributes):
621
        self = None
1✔
622
        alt_names = []
1✔
623

624
        for name, value in attributes.items():
1✔
625
            if not isinstance(value, providers.Self):
1✔
626
                continue
1✔
627

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

631
            if name != "__self__":
1✔
632
                alt_names.append(name)
1✔
633

634
            self = value
1✔
635

636
        if self:
1✔
637
            self.set_alt_names(alt_names)
1✔
638

639
        return self
1✔
640

641

642
class DeclarativeContainer(Container, metaclass=DeclarativeContainerMetaClass):
1✔
643
    """Declarative inversion of control container.
644

645
    .. code-block:: python
646

647
        class Services(DeclarativeContainer):
648
            auth = providers.Factory(AuthService)
649
            users = providers.Factory(UsersService,
650
                                      auth_service=auth)
651
    """
652

653
    __IS_CONTAINER__ = True
1✔
654

655
    provider_type = providers.Provider
1✔
656
    """Type of providers that could be placed in container.
657

658
    :type: type
659
    """
660

661
    instance_type = DynamicContainer
1✔
662
    """Type of container that is returned on instantiating declarative
663
    container.
664

665
    :type: type
666
    """
667

668
    containers = dict()
1✔
669
    """Read-only dictionary of all nested containers.
670

671
    :type: dict[str, :py:class:`DeclarativeContainer`]
672
    """
673

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

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

680
    wiring_config = WiringConfiguration()
1✔
681
    """Wiring configuration.
682

683
    :type: WiringConfiguration
684
    """
685

686
    auto_load_config = True
1✔
687
    """Automatically load configuration when the container is created.
688

689
    :type: bool
690
    """
691

692
    cls_providers = dict()
1✔
693
    """Read-only dictionary of current container providers.
694

695
    :type: dict[str, :py:class:`dependency_injector.providers.Provider`]
696
    """
697

698
    inherited_providers = dict()
1✔
699
    """Read-only dictionary of inherited providers.
700

701
    :type: dict[str, :py:class:`dependency_injector.providers.Provider`]
702
    """
703

704
    overridden = tuple()
1✔
705
    """Tuple of overriding containers.
706

707
    :type: tuple[:py:class:`DeclarativeContainer`]
708
    """
709

710
    __self__ = None
1✔
711
    """Provider that provides current container.
712

713
    :type: :py:class:`dependency_injector.providers.Provider`
714
    """
715

716
    def __new__(cls, **overriding_providers):
1✔
717
        """Constructor.
718

719
        :return: Dynamic container with copy of all providers.
720
        :rtype: :py:class:`DynamicContainer`
721
        """
722
        container = cls.instance_type()
1✔
723
        container.provider_type = cls.provider_type
1✔
724
        container.wiring_config = copy_module.deepcopy(cls.wiring_config)
1✔
725
        container.declarative_parent = cls
1✔
726

727
        copied_providers = providers.deepcopy({ **cls.providers, **{"@@self@@": cls.__self__}})
1✔
728
        copied_self = copied_providers.pop("@@self@@")
1✔
729
        copied_self.set_container(container)
1✔
730

731
        container.__self__ = copied_self
1✔
732
        for name in copied_self.alt_names:
1✔
733
            container.set_provider(name, copied_self)
1✔
734

735
        for name, provider in copied_providers.items():
1✔
736
            container.set_provider(name, provider)
1✔
737

738
        if cls.auto_load_config:
1✔
739
            container.load_config()
1✔
740

741
        container.override_providers(**overriding_providers)
1✔
742
        container.apply_container_providers_overridings()
1✔
743

744
        if container.is_auto_wiring_enabled():
1✔
745
            container.wire()
1✔
746

747
        return container
1✔
748

749
    @classmethod
1✔
750
    def override(cls, object overriding):
751
        """Override current container by overriding container.
752

753
        :param overriding: Overriding container.
754
        :type overriding: :py:class:`DeclarativeContainer`
755

756
        :raise: :py:exc:`dependency_injector.errors.Error` if trying to
757
                override container by itself or its subclasses
758

759
        :rtype: None
760
        """
761
        if issubclass(cls, overriding):
1✔
762
            raise errors.Error("Container {0} could not be overridden "
1✔
763
                               "with itself or its subclasses".format(cls))
1✔
764

765
        cls.overridden += (overriding,)
1✔
766

767
        for name, provider in overriding.cls_providers.items():
1✔
768
            try:
1✔
769
                getattr(cls, name).override(provider)
1✔
770
            except AttributeError:
1✔
771
                pass
772

773
    @classmethod
1✔
774
    def reset_last_overriding(cls):
775
        """Reset last overriding provider for each container providers.
776

777
        :rtype: None
778
        """
779
        if not cls.overridden:
1✔
780
            raise errors.Error("Container {0} is not overridden".format(cls))
1✔
781

782
        cls.overridden = cls.overridden[:-1]
1✔
783

784
        for provider in cls.providers.values():
1✔
785
            provider.reset_last_overriding()
1✔
786

787
    @classmethod
1✔
788
    def reset_override(cls):
789
        """Reset all overridings for each container providers.
790

791
        :rtype: None
792
        """
793
        cls.overridden = tuple()
1✔
794

795
        for provider in cls.providers.values():
1✔
796
            provider.reset_override()
1✔
797

798

799
class SingletonResetContext:
1✔
800

801
    def __init__(self, container):
1✔
802
        self._container = container
1✔
803

804
    def __enter__(self):
1✔
805
        return self._container
1✔
806

807
    def __exit__(self, *_):
1✔
808
        self._container.reset_singletons()
1✔
809

810

811

812
class ProvidersOverridingContext:
1✔
813

814
    def __init__(self, container, overridden_providers):
1✔
815
        self._container = container
1✔
816
        self._overridden_providers = overridden_providers
1✔
817

818
    def __enter__(self):
1✔
819
        return self._container
1✔
820

821
    def __exit__(self, *_):
1✔
822
        for provider in self._overridden_providers:
1✔
823
            provider.reset_last_overriding()
1✔
824

825

826
def override(object container):
1✔
827
    """:py:class:`DeclarativeContainer` overriding decorator.
828

829
    :param container: Container that should be overridden by decorated
830
                      container.
831
    :type container: :py:class:`DeclarativeContainer`
832

833
    :return: Declarative container overriding decorator.
834
    :rtype: callable(:py:class:`DeclarativeContainer`)
835
    """
836
    def _decorator(object overriding_container):
1✔
837
        """Overriding decorator."""
838
        container.override(overriding_container)
1✔
839
        return overriding_container
1✔
840
    return _decorator
1✔
841

842

843
def copy(object base_container):
1✔
844
    """:py:class:`DeclarativeContainer` copying decorator.
845

846
    This decorator copies all providers from provided container to decorated one.
847
    If one of the decorated container providers matches to source container
848
    providers by name, it would be replaced by reference.
849

850
    :param base_container: Container that should be copied by decorated container.
851
    :type base_container: :py:class:`DeclarativeContainer`
852

853
    :return: Declarative container copying decorator.
854
    :rtype: callable(:py:class:`DeclarativeContainer`)
855
    """
856
    def _get_memo_for_matching_names(new_providers, base_providers):
1✔
857
        memo = {}
1✔
858
        for new_provider_name, new_provider in new_providers.items():
1✔
859
            if new_provider_name not in base_providers:
1✔
860
                continue
1✔
861
            source_provider = base_providers[new_provider_name]
1✔
862
            memo[id(source_provider)] = new_provider
1✔
863

864
            if hasattr(new_provider, "providers") and hasattr(source_provider, "providers"):
1✔
865
                sub_memo = _get_memo_for_matching_names(new_provider.providers, source_provider.providers)
1✔
866
                memo.update(sub_memo)
1✔
867
        return memo
1✔
868

869
    def _decorator(new_container):
1✔
870
        memo = {}
1✔
871
        memo.update(_get_memo_for_matching_names(new_container.cls_providers, base_container.providers))
1✔
872

873
        new_providers = {}
1✔
874
        new_providers.update(providers.deepcopy(base_container.providers, memo))
1✔
875
        new_providers.update(providers.deepcopy(new_container.cls_providers, memo))
1✔
876

877
        for name, provider in new_providers.items():
1✔
878
            setattr(new_container, name, provider)
1✔
879
        return new_container
1✔
880

881
    return _decorator
1✔
882

883

884
cpdef bint is_container(object instance):
1✔
885
    """Check if instance is container instance.
886

887
    :param instance: Instance to be checked.
888
    :type instance: object
889

890
    :rtype: bool
891
    """
892
    return getattr(instance, "__IS_CONTAINER__", False) is True
1✔
893

894

895
cpdef object _check_provider_type(object container, object provider):
1✔
896
    if not isinstance(provider, container.provider_type):
1✔
897
        raise errors.Error("{0} can contain only {1} "
1✔
898
                           "instances".format(container, container.provider_type))
1✔
899

900

901
cpdef bint _any_relative_string_imports_in(object modules):
1✔
902
    for module in modules:
1✔
903
        if not isinstance(module, str):
1✔
904
            continue
1✔
905
        if module.startswith("."):
1✔
906
            return True
1✔
907
    else:
908
        return False
1✔
909

910

911
cpdef list _resolve_string_imports(object modules, object from_package):
1✔
912
    return [
1✔
913
        importlib.import_module(module, from_package) if isinstance(module, str) else module
1✔
914
        for module in modules
1✔
915
    ]
916

917

918
cpdef object _resolve_calling_package_name():
1✔
919
    stack = inspect.stack()
1✔
920
    pre_last_frame = stack[0]
1✔
921
    module = inspect.getmodule(pre_last_frame[0])
1✔
922
    return module.__package__
1✔
923

924

925
cpdef object _resolve_package_name_from_cls(cls):
1✔
926
    module = importlib.import_module(cls.__module__)
1✔
927
    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