• 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

95.7
/src/dependency_injector/providers.pyx
1
"""Providers module."""
1✔
2

3
from __future__ import absolute_import
4

5
import asyncio
1✔
6
import builtins
1✔
7
import copy
1✔
8
import errno
1✔
9
import functools
1✔
10
import importlib
1✔
11
import inspect
1✔
12
import json
1✔
13
import os
1✔
14
import re
1✔
15
import sys
1✔
16
import threading
1✔
17
import warnings
1✔
18
from asyncio import ensure_future
1✔
19
from configparser import ConfigParser as IniConfigParser
1✔
20
from contextlib import asynccontextmanager, contextmanager
1✔
21
from contextvars import ContextVar
1✔
22
from inspect import isasyncgenfunction, isgeneratorfunction
1✔
23

24
try:
1✔
25
    from inspect import _is_coroutine_mark as _is_coroutine_marker
1✔
26
except ImportError:
×
27
    try:
×
28
        # Python >=3.12.0,<3.12.5
29
        from inspect import _is_coroutine_marker
×
30
    except ImportError:
×
31
        _is_coroutine_marker = True
×
32

33
try:
1✔
34
    from asyncio.coroutines import _is_coroutine
1✔
35
except ImportError:
×
36
    _is_coroutine = True
×
37

38
try:
1✔
39
    import yaml
1✔
40
except ImportError:
×
41
    yaml = None
×
42

43
has_pydantic_settings = True
1✔
44
cdef bint pydantic_v1 = False
1✔
45
cdef str pydantic_module = "pydantic_settings"
1✔
46
cdef str pydantic_extra = "pydantic2"
1✔
47

48
try:
1✔
49
    from pydantic_settings import BaseSettings as PydanticSettings
1✔
50
except ImportError:
×
51
    try:
×
52
        # pydantic-settings requires pydantic v2,
53
        # so it is safe to assume that we're dealing with v1:
54
        from pydantic import BaseSettings as PydanticSettings
×
55
        pydantic_v1 = True
×
56
        pydantic_module = "pydantic"
×
57
        pydantic_extra = "pydantic"
×
58
    except ImportError:
×
59
        # if it is present, ofc
60
        has_pydantic_settings = False
×
61

62

63
from .errors import (
1✔
64
    Error,
1✔
65
    NoSuchProviderError,
66
    NonCopyableArgumentError,
67
)
68

69
cimport cython
70

71

72
config_env_marker_pattern = re.compile(
1✔
73
    r"\${(?P<name>[^}^{:]+)(?P<separator>:?)(?P<default>.*?)}",
74
)
75

76
cdef str _resolve_config_env_markers(config_content: str, envs_required: bool):
1✔
77
    """Replace environment variable markers with their values."""
78
    findings = list(config_env_marker_pattern.finditer(config_content))
1✔
79

80
    for match in reversed(findings):
1✔
81
        env_name = match.group("name")
1✔
82
        has_default = match.group("separator") == ":"
1✔
83

84
        value = os.getenv(env_name)
1✔
85
        if value is None:
1✔
86
            if not has_default and envs_required:
1✔
87
                raise ValueError(f"Missing required environment variable \"{env_name}\"")
1✔
88
            value = match.group("default")
1✔
89

90
        span_min, span_max = match.span()
1✔
91
        config_content = f"{config_content[:span_min]}{value}{config_content[span_max:]}"
1✔
92
    return config_content
1✔
93

94

95
cdef object _parse_ini_file(filepath, envs_required: bool | None):
1✔
96
    parser = IniConfigParser()
1✔
97

98
    with open(filepath) as config_file:
1✔
99
        config_string = config_file.read()
1✔
100

101
        if envs_required is not None:
1✔
102
            config_string = _resolve_config_env_markers(
1✔
103
                config_string,
1✔
104
                envs_required=envs_required,
105
            )
106
    parser.read_string(config_string)
1✔
107
    return parser
1✔
108

109

110
if yaml:
1✔
111
    class YamlLoader(yaml.SafeLoader):
1✔
112
        """YAML loader.
113

114
        This loader mimics ``yaml.SafeLoader``.
115
        """
116
else:
117
    class YamlLoader:
×
118
        """YAML loader.
119

120
        This loader mimics ``yaml.SafeLoader``.
121
        """
122

123

124
UNDEFINED = object()
1✔
125

126
cdef int ASYNC_MODE_UNDEFINED = 0
1✔
127
cdef int ASYNC_MODE_ENABLED = 1
1✔
128
cdef int ASYNC_MODE_DISABLED = 2
1✔
129

130
cdef set __iscoroutine_typecache = set()
1✔
131
cdef tuple __COROUTINE_TYPES = asyncio.coroutines._COROUTINE_TYPES
1✔
132

133
cdef dict pydantic_settings_to_dict(settings, dict kwargs):
1✔
134
    if not has_pydantic_settings:
1✔
135
        raise Error(
1✔
136
            f"Unable to load pydantic configuration - {pydantic_module} is not installed. "
1✔
137
            "Install pydantic or install Dependency Injector with pydantic extras: "
138
            f"\"pip install dependency-injector[{pydantic_extra}]\""
1✔
139
        )
140

141
    if isinstance(settings, type) and issubclass(settings, PydanticSettings):
1✔
142
        raise Error(
1✔
143
            "Got settings class, but expect instance: "
144
            "instead \"{0}\" use \"{0}()\"".format(settings.__name__)
1✔
145
        )
146

147
    if not isinstance(settings, PydanticSettings):
1✔
148
        raise Error(
1✔
149
            f"Unable to recognize settings instance, expect \"{pydantic_module}.BaseSettings\", "
1✔
150
            f"got {settings} instead"
1✔
151
        )
152

153
    if pydantic_v1:
1✔
154
        return settings.dict(**kwargs)
×
155

156
    return settings.model_dump(mode="python", **kwargs)
1✔
157

158

159
cdef class Provider:
160
    """Base provider class.
161

162
    :py:class:`Provider` is callable (implements ``__call__`` method). Every
163
    call to provider object returns provided result, according to the providing
164
    strategy of particular provider. This ``callable`` functionality is a
165
    regular part of providers API and it should be the same for all provider
166
    subclasses.
167

168
    Implementation of particular providing strategy should be done in
169
    :py:meth:`Provider._provide` of :py:class:`Provider` subclass. Current
170
    method is called every time when not overridden provider is called.
171

172
    :py:class:`Provider` implements provider overriding logic that should be
173
    also common for all providers:
174

175
    .. code-block:: python
176

177
        provider1 = Factory(SomeClass)
178
        provider2 = Factory(ChildSomeClass)
179

180
        provider1.override(provider2)
181

182
        some_instance = provider1()
183
        assert isinstance(some_instance, ChildSomeClass)
184

185
    Also :py:class:`Provider` implements helper function for creating its
186
    delegates:
187

188
    .. code-block:: python
189

190
        provider = Factory(object)
191
        delegate = provider.delegate()
192

193
        delegated = delegate()
194

195
        assert provider is delegated
196

197
    All providers should extend this class.
198

199
    .. py:attribute:: overridden
200
       :noindex:
201

202
        Tuple of overriding providers, if any.
203

204
        :type: tuple[:py:class:`Provider`] | None
205
    """
206

207
    __IS_PROVIDER__ = True
1✔
208

209
    overriding_lock = threading.RLock()
1✔
210
    """Overriding reentrant lock.
211

212
    :type: :py:class:`threading.RLock`
213
    """
214

215
    def __init__(self):
216
        """Initializer."""
217
        self._overridden = tuple()
1✔
218
        self._last_overriding = None
1✔
219
        self._overrides = tuple()
1✔
220
        self._async_mode = ASYNC_MODE_UNDEFINED
1✔
221
        super(Provider, self).__init__()
1✔
222

223
    def __call__(self, *args, **kwargs):
224
        """Return provided object.
225

226
        Callable interface implementation.
227
        """
228
        if self._last_overriding is not None:
1✔
229
            result = self._last_overriding(*args, **kwargs)
1✔
230
        else:
231
            result = self._provide(args, kwargs)
1✔
232

233
        if self._async_mode == ASYNC_MODE_DISABLED:
1✔
234
            return result
1✔
235
        elif self._async_mode == ASYNC_MODE_ENABLED:
1✔
236
            if __is_future_or_coroutine(result):
1✔
237
                return result
1✔
238
            return __future_result(result)
1✔
239
        elif self._async_mode == ASYNC_MODE_UNDEFINED:
1✔
240
            if __is_future_or_coroutine(result):
1✔
241
                self.enable_async_mode()
1✔
242
            else:
243
                self.disable_async_mode()
1✔
244
            return result
1✔
245

246
    def __deepcopy__(self, memo):
1✔
247
        """Create and return full copy of provider."""
248
        copied = memo.get(id(self))
1✔
249
        if copied is not None:
1✔
250
            return copied
×
251

252
        copied = _memorized_duplicate(self, memo)
1✔
253
        self._copy_overridings(copied, memo)
1✔
254
        return copied
1✔
255

256
    @classmethod
1✔
257
    def __class_getitem__(cls, item):
258
        return cls
1✔
259

260
    def __str__(self):
261
        """Return string representation of provider.
262

263
        :rtype: str
264
        """
265
        return represent_provider(provider=self, provides=None)
1✔
266

267
    def __repr__(self):
268
        """Return string representation of provider.
269

270
        :rtype: str
271
        """
272
        return self.__str__()
1✔
273

274
    @property
275
    def overridden(self):
276
        """Return tuple of overriding providers."""
277
        with self.overriding_lock:
1✔
278
            return self._overridden
1✔
279

280
    @property
281
    def last_overriding(self):
282
        """Return last overriding provider.
283

284
        If provider is not overridden, then None is returned.
285
        """
286
        return self._last_overriding
1✔
287

288
    def override(self, provider):
1✔
289
        """Override provider with another provider.
290

291
        :param provider: Overriding provider.
292
        :type provider: :py:class:`Provider`
293

294
        :raise: :py:exc:`dependency_injector.errors.Error`
295

296
        :return: Overriding context.
297
        :rtype: :py:class:`OverridingContext`
298
        """
299
        if provider is self:
1✔
300
            raise Error("Provider {0} could not be overridden with itself".format(self))
1✔
301

302
        if not is_provider(provider):
1✔
303
            provider = Object(provider)
1✔
304

305
        with self.overriding_lock:
1✔
306
            self._overridden += (provider,)
1✔
307
            self._last_overriding = provider
1✔
308
            provider.register_overrides(self)
1✔
309

310
        return OverridingContext(self, provider)
1✔
311

312
    def reset_last_overriding(self):
1✔
313
        """Reset last overriding provider.
314

315
        :raise: :py:exc:`dependency_injector.errors.Error` if provider is not
316
                overridden.
317

318
        :rtype: None
319
        """
320
        with self.overriding_lock:
1✔
321
            if len(self._overridden) == 0:
1✔
322
                raise Error("Provider {0} is not overridden".format(str(self)))
1✔
323

324
            self._last_overriding.unregister_overrides(self)
1✔
325

326
            self._overridden = self._overridden[:-1]
1✔
327
            try:
1✔
328
                self._last_overriding = self._overridden[-1]
1✔
329
            except IndexError:
1✔
330
                self._last_overriding = None
1✔
331

332
    def reset_override(self):
1✔
333
        """Reset all overriding providers.
334

335
        :rtype: None
336
        """
337
        with self.overriding_lock:
1✔
338
            for provider in self._overridden:
1✔
339
                provider.unregister_overrides(self)
1✔
340
            self._overridden = tuple()
1✔
341
            self._last_overriding = None
1✔
342

343
    @property
344
    def overrides(self):
345
        """Return providers that are overridden by the current provider."""
346
        return self._overrides
1✔
347

348
    def register_overrides(self, provider):
1✔
349
        """Register provider that overrides current provider."""
350
        self._overrides =  tuple(set(self._overrides + (provider,)))
1✔
351

352
    def unregister_overrides(self, provider):
1✔
353
        """Unregister provider that overrides current provider."""
354
        overrides = set(self._overrides)
1✔
355
        if provider in overrides:
1✔
356
            overrides.remove(provider)
1✔
357
        self._overrides = tuple(overrides)
1✔
358

359
    def async_(self, *args, **kwargs):
1✔
360
        """Return provided object asynchronously.
361

362
        This method is a synonym of __call__().
363
        It provides typing stubs for correct type checking with
364
        `await` expression:
365

366
        .. code-block:: python
367

368
            database_provider: Provider[DatabaseConnection] = Resource(init_db_async)
369

370
            async def main():
371
                db: DatabaseConnection = await database_provider.async_()
372
                ...
373
        """
374
        return self.__call__(*args, **kwargs)
1✔
375

376
    def delegate(self):
1✔
377
        """Return provider delegate.
378

379
        :rtype: :py:class:`Delegate`
380
        """
381
        warnings.warn(
1✔
382
            "Method \".delegate()\" is deprecated since version 4.0.0. "
383
            "Use \".provider\" attribute instead.",
384
            category=DeprecationWarning,
1✔
385
        )
386
        return Delegate(self)
1✔
387

388
    @property
389
    def provider(self):
390
        """Return provider"s delegate.
391

392
        :rtype: :py:class:`Delegate`
393
        """
394
        return Delegate(self)
1✔
395

396
    @property
397
    def provided(self):
398
        """Return :py:class:`ProvidedInstance` provider."""
399
        return ProvidedInstance(self)
1✔
400

401
    def enable_async_mode(self):
1✔
402
        """Enable async mode."""
403
        self._async_mode = ASYNC_MODE_ENABLED
1✔
404

405
    def disable_async_mode(self):
1✔
406
        """Disable async mode."""
407
        self._async_mode = ASYNC_MODE_DISABLED
1✔
408

409
    def reset_async_mode(self):
1✔
410
        """Reset async mode.
411

412
        Provider will automatically set the mode on the next call.
413
        """
414
        self._async_mode = ASYNC_MODE_UNDEFINED
1✔
415

416
    cpdef bint is_async_mode_enabled(self):
1✔
417
        """Check if async mode is enabled."""
418
        return self._async_mode == ASYNC_MODE_ENABLED
1✔
419

420
    cpdef bint is_async_mode_disabled(self):
1✔
421
        """Check if async mode is disabled."""
422
        return self._async_mode == ASYNC_MODE_DISABLED
1✔
423

424
    cpdef bint is_async_mode_undefined(self):
1✔
425
        """Check if async mode is undefined."""
426
        return self._async_mode == ASYNC_MODE_UNDEFINED
1✔
427

428
    @property
429
    def related(self):
430
        """Return related providers generator."""
431
        yield from self.overridden
1✔
432

433
    def traverse(self, types=None):
1✔
434
        """Return providers traversal generator."""
435
        return traverse(*self.related, types=types)
1✔
436

437
    cpdef object _provide(self, tuple args, dict kwargs):
1✔
438
        """Providing strategy implementation.
439

440
        Abstract protected method that implements providing strategy of
441
        particular provider. Current method is called every time when not
442
        overridden provider is called. Need to be overridden in subclasses.
443
        """
444
        raise NotImplementedError()
1✔
445

446
    cpdef void _copy_overridings(self, Provider copied, dict memo):
1✔
447
        """Copy provider overridings to a newly copied provider."""
448
        copied._overridden = deepcopy(self._overridden, memo)
1✔
449
        copied._last_overriding = deepcopy(self._last_overriding, memo)
1✔
450
        copied._overrides = deepcopy(self._overrides, memo)
1✔
451

452

453
cdef class Object(Provider):
454
    """Object provider returns provided instance "as is".
455

456
    .. py:attribute:: provides
457

458
        Value that have to be provided.
459

460
        :type: object
461
    """
462

463
    def __init__(self, provides=None):
464
        """Initialize provider."""
465
        self._provides = None
1✔
466
        self.set_provides(provides)
1✔
467
        super(Object, self).__init__()
1✔
468

469
    def __deepcopy__(self, memo):
1✔
470
        """Create and return full copy of provider."""
471
        copied = memo.get(id(self))
1✔
472
        if copied is not None:
1✔
473
            return copied
×
474

475
        copied = _memorized_duplicate(self, memo)
1✔
476
        copied.set_provides(self.provides)
1✔
477

478
        self._copy_overridings(copied, memo)
1✔
479

480
        return copied
1✔
481

482
    def __str__(self):
483
        """Return string representation of provider.
484

485
        :rtype: str
486
        """
487
        return represent_provider(provider=self, provides=self._provides)
1✔
488

489
    def __repr__(self):
490
        """Return string representation of provider.
491

492
        :rtype: str
493
        """
494
        return self.__str__()
1✔
495

496
    @property
497
    def provides(self):
498
        """Return provider provides."""
499
        return self._provides
1✔
500

501
    def set_provides(self, provides):
1✔
502
        """Set provider provides."""
503
        self._provides = provides
1✔
504
        return self
1✔
505

506
    @property
507
    def related(self):
508
        """Return related providers generator."""
509
        if isinstance(self._provides, Provider):
1✔
510
            yield self._provides
1✔
511
        yield from super().related
1✔
512

513
    cpdef object _provide(self, tuple args, dict kwargs):
1✔
514
        """Return provided instance.
515

516
        :param args: Tuple of context positional arguments.
517
        :type args: tuple[object]
518

519
        :param kwargs: Dictionary of context keyword arguments.
520
        :type kwargs: dict[str, object]
521

522
        :rtype: object
523
        """
524
        return self._provides
1✔
525

526

527
cdef class Self(Provider):
528
    """Self provider returns own container."""
529

530
    def __init__(self, container=None):
531
        """Initialize provider."""
532
        self._container = container
1✔
533
        self._alt_names = tuple()
1✔
534
        super().__init__()
1✔
535

536
    def __deepcopy__(self, memo):
1✔
537
        """Create and return full copy of provider."""
538
        copied = memo.get(id(self))
1✔
539
        if copied is not None:
1✔
540
            return copied
1✔
541

542
        copied = _memorized_duplicate(self, memo)
1✔
543
        copied.set_container(deepcopy(self._container, memo))
1✔
544
        copied.set_alt_names(self._alt_names)
1✔
545
        self._copy_overridings(copied, memo)
1✔
546
        return copied
1✔
547

548
    def __str__(self):
549
        """Return string representation of provider.
550

551
        :rtype: str
552
        """
553
        return represent_provider(provider=self, provides=self._container)
1✔
554

555
    def __repr__(self):
556
        """Return string representation of provider.
557

558
        :rtype: str
559
        """
560
        return self.__str__()
1✔
561

562
    def set_container(self, container):
1✔
563
        self._container = container
1✔
564

565
    def set_alt_names(self, alt_names):
1✔
566
        self._alt_names = tuple(set(alt_names))
1✔
567

568
    @property
569
    def alt_names(self):
570
        return self._alt_names
1✔
571

572
    cpdef object _provide(self, tuple args, dict kwargs):
1✔
573
        return self._container
1✔
574

575

576
cdef class Delegate(Provider):
577
    """Delegate provider returns provider "as is".
578

579
    .. py:attribute:: provides
580

581
        Value that have to be provided.
582

583
        :type: object
584
    """
585

586
    def __init__(self, provides=None):
587
        """Initialize provider."""
588
        self._provides = None
1✔
589
        self.set_provides(provides)
1✔
590
        super(Delegate, self).__init__()
1✔
591

592
    def __deepcopy__(self, memo):
1✔
593
        """Create and return full copy of provider."""
594
        copied = memo.get(id(self))
1✔
595
        if copied is not None:
1✔
596
            return copied
×
597

598
        copied = _memorized_duplicate(self, memo)
1✔
599
        copied.set_provides(_copy_if_provider(self.provides, memo))
1✔
600
        self._copy_overridings(copied, memo)
1✔
601

602
        return copied
1✔
603

604
    def __str__(self):
605
        """Return string representation of provider.
606

607
        :rtype: str
608
        """
609
        return represent_provider(provider=self, provides=self._provides)
1✔
610

611
    def __repr__(self):
612
        """Return string representation of provider.
613

614
        :rtype: str
615
        """
616
        return self.__str__()
1✔
617

618
    @property
619
    def provides(self):
620
        """Return provider provides."""
621
        return self._provides
1✔
622

623
    def set_provides(self, provides):
1✔
624
        """Set provider provides."""
625
        if provides:
1✔
626
            provides = ensure_is_provider(provides)
1✔
627
        self._provides = provides
1✔
628
        return self
1✔
629

630
    @property
631
    def related(self):
632
        """Return related providers generator."""
633
        yield self._provides
1✔
634
        yield from super().related
1✔
635

636
    cpdef object _provide(self, tuple args, dict kwargs):
1✔
637
        """Return provided instance.
638

639
        :param args: Tuple of context positional arguments.
640
        :type args: tuple[object]
641

642
        :param kwargs: Dictionary of context keyword arguments.
643
        :type kwargs: dict[str, object]
644

645
        :rtype: object
646
        """
647
        return self._provides
1✔
648

649

650
cdef class Aggregate(Provider):
651
    """Providers aggregate.
652

653
    :py:class:`Aggregate` is a delegated provider, meaning that it is
654
    injected "as is".
655

656
    All aggregated providers can be retrieved as a read-only
657
    dictionary :py:attr:`Aggregate.providers` or as an attribute of
658
    :py:class:`Aggregate`, e.g. ``aggregate.provider``.
659
    """
660

661
    __IS_DELEGATED__ = True
1✔
662

663
    def __init__(self, provider_dict=None, **provider_kwargs):
664
        """Initialize provider."""
665
        self._providers = {}
1✔
666
        self.set_providers(provider_dict, **provider_kwargs)
1✔
667
        super().__init__()
1✔
668

669
    def __deepcopy__(self, memo):
1✔
670
        """Create and return full copy of provider."""
671
        copied = memo.get(id(self))
1✔
672
        if copied is not None:
1✔
673
            return copied
1✔
674

675
        copied = _memorized_duplicate(self, memo)
1✔
676
        copied.set_providers(deepcopy(self.providers, memo))
1✔
677

678
        self._copy_overridings(copied, memo)
1✔
679

680
        return copied
1✔
681

682
    def __getattr__(self, factory_name):
683
        """Return aggregated provider."""
684
        return self.__get_provider(factory_name)
1✔
685

686
    def __str__(self):
687
        """Return string representation of provider.
688

689
        :rtype: str
690
        """
691
        return represent_provider(provider=self, provides=self.providers)
1✔
692

693
    @property
694
    def providers(self):
695
        """Return dictionary of providers, read-only.
696

697
        Alias for ``.factories`` attribute.
698
        """
699
        return dict(self._providers)
1✔
700

701
    def set_providers(self, provider_dict=None, **provider_kwargs):
1✔
702
        """Set providers.
703

704
        Alias for ``.set_factories()`` method.
705
        """
706
        providers = {}
1✔
707
        providers.update(provider_kwargs)
1✔
708
        if provider_dict:
1✔
709
            providers.update(provider_dict)
1✔
710

711
        for provider in providers.values():
1✔
712
            if not is_provider(provider):
1✔
713
                raise Error(
1✔
714
                    "{0} can aggregate only instances of {1}, given - {2}".format(
1✔
715
                        self.__class__,
1✔
716
                        Provider,
717
                        provider,
1✔
718
                    ),
719
                )
720

721
        self._providers = providers
1✔
722
        return self
1✔
723

724
    def override(self, _):
1✔
725
        """Override provider with another provider.
726

727
        :raise: :py:exc:`dependency_injector.errors.Error`
728

729
        :return: Overriding context.
730
        :rtype: :py:class:`OverridingContext`
731
        """
732
        raise Error("{0} providers could not be overridden".format(self.__class__))
1✔
733

734
    @property
735
    def related(self):
736
        """Return related providers generator."""
737
        yield from self._providers.values()
1✔
738
        yield from super().related
1✔
739

740
    cpdef object _provide(self, tuple args, dict kwargs):
1✔
741
        try:
1✔
742
            provider_name = args[0]
1✔
743
        except IndexError:
1✔
744
            try:
1✔
745
                provider_name = kwargs.pop("factory_name")
1✔
746
            except KeyError:
1✔
747
                raise TypeError("Missing 1st required positional argument: \"provider_name\"")
1✔
748
        else:
749
            args = args[1:]
1✔
750

751
        return self.__get_provider(provider_name)(*args, **kwargs)
1✔
752

753
    cdef Provider __get_provider(self, object provider_name):
1✔
754
        if provider_name not in self._providers:
1✔
755
            raise NoSuchProviderError("{0} does not contain provider with name {1}".format(self, provider_name))
1✔
756
        return <Provider> self._providers[provider_name]
1✔
757

758

759
cdef class Dependency(Provider):
760
    """:py:class:`Dependency` provider describes dependency interface.
761

762
    This provider is used for description of dependency interface. That might
763
    be useful when dependency could be provided in the client"s code only,
764
    but its interface is known. Such situations could happen when required
765
    dependency has non-deterministic list of dependencies itself.
766

767
    .. code-block:: python
768

769
        database_provider = Dependency(sqlite3.dbapi2.Connection)
770
        database_provider.override(Factory(sqlite3.connect, ":memory:"))
771

772
        database = database_provider()
773

774
    .. py:attribute:: instance_of
775
       :noindex:
776

777
        Class of required dependency.
778

779
        :type: type
780
   """
781

782
    def __init__(self, object instance_of=object, default=None):
783
        """Initialize provider."""
784
        self._instance_of = None
1✔
785
        self.set_instance_of(instance_of)
1✔
786

787
        self._default = None
1✔
788
        self.set_default(default)
1✔
789

790
        self._parent = None
1✔
791

792
        super(Dependency, self).__init__()
1✔
793

794
    def __deepcopy__(self, memo):
1✔
795
        """Create and return full copy of provider."""
796
        copied = memo.get(id(self))
1✔
797
        if copied is not None:
1✔
798
            return copied
×
799

800
        copied = _memorized_duplicate(self, memo)
1✔
801
        copied.set_instance_of(self.instance_of)
1✔
802
        copied.set_default(deepcopy(self.default, memo))
1✔
803

804
        self._copy_parent(copied, memo)
1✔
805
        self._copy_overridings(copied, memo)
1✔
806

807
        return copied
1✔
808

809
    def __call__(self, *args, **kwargs):
810
        """Return provided instance.
811

812
        :raise: :py:exc:`dependency_injector.errors.Error`
813

814
        :rtype: object
815
        """
816
        if self._last_overriding:
1✔
817
            result = self._last_overriding(*args, **kwargs)
1✔
818
        elif self._default:
1✔
819
            result = self._default(*args, **kwargs)
1✔
820
        else:
821
            self._raise_undefined_error()
1✔
822

823
        if self._async_mode == ASYNC_MODE_DISABLED:
1✔
824
            self._check_instance_type(result)
1✔
825
            return result
1✔
826
        elif self._async_mode == ASYNC_MODE_ENABLED:
1✔
827
            if __is_future_or_coroutine(result):
1✔
828
                future_result = asyncio.Future()
1✔
829
                result = asyncio.ensure_future(result)
1✔
830
                result.add_done_callback(functools.partial(self._async_provide, future_result))
1✔
831
                return future_result
1✔
832
            else:
833
                self._check_instance_type(result)
1✔
834
                return __future_result(result)
1✔
835
        elif self._async_mode == ASYNC_MODE_UNDEFINED:
1✔
836
            if __is_future_or_coroutine(result):
1✔
837
                self.enable_async_mode()
1✔
838

839
                future_result = asyncio.Future()
1✔
840
                result = asyncio.ensure_future(result)
1✔
841
                result.add_done_callback(functools.partial(self._async_provide, future_result))
1✔
842
                return future_result
1✔
843
            else:
844
                self.disable_async_mode()
1✔
845
                self._check_instance_type(result)
1✔
846
                return result
1✔
847

848
    def __getattr__(self, name):
849
        if self._last_overriding:
1✔
850
            return getattr(self._last_overriding, name)
1✔
851
        elif self._default:
1✔
852
            return getattr(self._default, name)
1✔
853
        raise AttributeError(f"Provider \"{self.__class__.__name__}\" has no attribute \"{name}\"")
1✔
854

855
    def __str__(self):
856
        """Return string representation of provider.
857

858
        :rtype: str
859
        """
860
        name = f"<{self.__class__.__module__}.{self.__class__.__name__}"
1✔
861
        name += f"({repr(self._instance_of)}) at {hex(id(self))}"
1✔
862
        if self.parent_name:
1✔
863
            name += f", container name: \"{self.parent_name}\""
1✔
864
        name += f">"
1✔
865
        return name
1✔
866

867
    def __repr__(self):
868
        """Return string representation of provider.
869

870
        :rtype: str
871
        """
872
        return self.__str__()
1✔
873

874
    @property
875
    def instance_of(self):
876
        """Return type."""
877
        return self._instance_of
1✔
878

879
    def set_instance_of(self, instance_of):
1✔
880
        """Set type."""
881
        if not isinstance(instance_of, type):
1✔
882
            raise TypeError(
1✔
883
                f"\"instance_of\" is not a class (got {instance_of!r}))",
1✔
884
            )
885
        self._instance_of = instance_of
1✔
886
        return self
1✔
887

888
    @property
889
    def default(self):
890
        """Return default provider."""
891
        return self._default
1✔
892

893
    def set_default(self, default):
1✔
894
        """Set type."""
895
        if default is not None and not isinstance(default, Provider):
1✔
896
            default = Object(default)
1✔
897
        self._default = default
1✔
898
        return self
1✔
899

900
    @property
901
    def is_defined(self):
902
        """Return True if dependency is defined."""
903
        return self._last_overriding is not None or self._default is not None
1✔
904

905
    def provided_by(self, provider):
1✔
906
        """Set external dependency provider.
907

908
        :param provider: Provider that provides required dependency.
909
        :type provider: :py:class:`Provider`
910

911
        :rtype: None
912
        """
913
        return self.override(provider)
1✔
914

915
    @property
916
    def related(self):
917
        """Return related providers generator."""
918
        if self._default:
1✔
919
            yield self._default
1✔
920
        yield from super().related
1✔
921

922
    @property
923
    def parent(self):
924
        """Return parent."""
925
        return self._parent
1✔
926

927
    @property
928
    def parent_name(self):
929
        """Return parent name."""
930
        if not self._parent:
1✔
931
            return None
1✔
932

933
        name = ""
1✔
934
        if self._parent.parent_name:
1✔
935
            name += f"{self._parent.parent_name}."
1✔
936
        name += f"{self._parent.resolve_provider_name(self)}"
1✔
937

938
        return name
1✔
939

940
    def assign_parent(self, parent):
1✔
941
        """Assign parent."""
942
        self._parent = parent
1✔
943

944
    def _copy_parent(self, copied, memo):
1✔
945
        _copy_parent(self, copied, memo)
1✔
946

947
    def _async_provide(self, future_result, future):
1✔
948
        try:
1✔
949
            instance = future.result()
1✔
950
            self._check_instance_type(instance)
1✔
951
        except Exception as exception:
1✔
952
            future_result.set_exception(exception)
1✔
953
        else:
954
            future_result.set_result(instance)
1✔
955

956
    def _check_instance_type(self, instance):
1✔
957
        if not isinstance(instance, self.instance_of):
1✔
958
            raise Error("{0} is not an instance of {1}".format(instance, self.instance_of))
1✔
959

960
    def _raise_undefined_error(self):
1✔
961
        if self.parent_name:
1✔
962
            raise Error(f"Dependency \"{self.parent_name}\" is not defined")
1✔
963
        raise Error("Dependency is not defined")
1✔
964

965

966
cdef class ExternalDependency(Dependency):
967
    """:py:class:`ExternalDependency` provider describes dependency interface.
968

969
    This provider is used for description of dependency interface. That might
970
    be useful when dependency could be provided in the client code only,
971
    but its interface is known. Such situations could happen when required
972
    dependency has non-deterministic list of dependencies itself.
973

974
    .. code-block:: python
975

976
        database_provider = ExternalDependency(sqlite3.dbapi2.Connection)
977
        database_provider.override(Factory(sqlite3.connect, ":memory:"))
978

979
        database = database_provider()
980

981
    .. deprecated:: 3.9
982

983
        Use :py:class:`Dependency` instead.
984

985
    .. py:attribute:: instance_of
986
       :noindex:
987

988
        Class of required dependency.
989

990
        :type: type
991
    """
992

993

994
cdef class DependenciesContainer(Object):
995
    """:py:class:`DependenciesContainer` provider provides set of dependencies.
996

997

998
    Dependencies container provider is used to implement late static binding
999
    for a set of providers of a particular container.
1000

1001
    Example code:
1002

1003
    .. code-block:: python
1004

1005
        class Adapters(containers.DeclarativeContainer):
1006
            email_sender = providers.Singleton(SmtpEmailSender)
1007

1008
        class TestAdapters(containers.DeclarativeContainer):
1009
            email_sender = providers.Singleton(EchoEmailSender)
1010

1011
        class UseCases(containers.DeclarativeContainer):
1012
            adapters = providers.DependenciesContainer()
1013

1014
            signup = providers.Factory(SignupUseCase,
1015
                                       email_sender=adapters.email_sender)
1016

1017
        use_cases = UseCases(adapters=Adapters)
1018
        # or
1019
        use_cases = UseCases(adapters=TestAdapters)
1020

1021
        # Another file
1022
        from .containers import use_cases
1023

1024
        use_case = use_cases.signup()
1025
        use_case.execute()
1026
    """
1027

1028
    def __init__(self, **dependencies):
1029
        """Initializer."""
1030
        for provider in dependencies.values():
1✔
1031
            if isinstance(provider, CHILD_PROVIDERS):
1✔
1032
                provider.assign_parent(self)
1✔
1033

1034
        self._providers = dependencies
1✔
1035
        self._parent = None
1✔
1036

1037
        super(DependenciesContainer, self).__init__(None)
1✔
1038

1039
    def __deepcopy__(self, memo):
1✔
1040
        """Create and return full copy of provider."""
1041
        cdef DependenciesContainer copied
1042

1043
        copied = memo.get(id(self))
1✔
1044
        if copied is not None:
1✔
1045
            return copied
×
1046

1047
        copied = <DependenciesContainer> _memorized_duplicate(self, memo)
1✔
1048
        copied._provides = deepcopy(self._provides, memo)
1✔
1049
        copied._providers = deepcopy(self._providers, memo)
1✔
1050
        self._copy_parent(copied, memo)
1✔
1051
        self._copy_overridings(copied, memo)
1✔
1052

1053
        return copied
1✔
1054

1055
    def __getattr__(self, name):
1056
        """Return dependency provider."""
1057
        if name.startswith("__") and name.endswith("__"):
1✔
1058
            raise AttributeError(
1✔
1059
                "'{cls}' object has no attribute "
1060
                "'{attribute_name}'".format(cls=self.__class__.__name__, attribute_name=name)
1✔
1061
            )
1062

1063
        provider = self._providers.get(name)
1✔
1064
        if not provider:
1✔
1065
            provider = Dependency()
1✔
1066
            provider.assign_parent(self)
1✔
1067

1068
            self._providers[name] = provider
1✔
1069

1070
            container = self.__call__()
1✔
1071
            if container:
1✔
1072
                dependency_provider = container.providers.get(name)
1✔
1073
                if dependency_provider:
1✔
1074
                    provider.override(dependency_provider)
1✔
1075

1076
        return provider
1✔
1077

1078
    @property
1079
    def providers(self):
1080
        """Read-only dictionary of dependency providers."""
1081
        return self._providers
1✔
1082

1083
    def override(self, provider):
1✔
1084
        """Override provider with another provider.
1085

1086
        :param provider: Overriding provider.
1087
        :type provider: :py:class:`Provider`
1088

1089
        :raise: :py:exc:`dependency_injector.errors.Error`
1090

1091
        :return: Overriding context.
1092
        :rtype: :py:class:`OverridingContext`
1093
        """
1094
        self._override_providers(container=provider)
1✔
1095
        return super(DependenciesContainer, self).override(provider)
1✔
1096

1097
    def reset_last_overriding(self):
1✔
1098
        """Reset last overriding provider.
1099

1100
        :raise: :py:exc:`dependency_injector.errors.Error` if provider is not
1101
                overridden.
1102

1103
        :rtype: None
1104
        """
1105
        for child in self._providers.values():
1✔
1106
            try:
1✔
1107
                child.reset_last_overriding()
1✔
1108
            except Error:
×
1109
                pass
×
1110
        super(DependenciesContainer, self).reset_last_overriding()
1✔
1111

1112
    def reset_override(self):
1✔
1113
        """Reset all overriding providers.
1114

1115
        :rtype: None
1116
        """
1117
        for child in self._providers.values():
1✔
1118
            child.reset_override()
1✔
1119
        super(DependenciesContainer, self).reset_override()
1✔
1120

1121
    @property
1122
    def related(self):
1123
        """Return related providers generator."""
1124
        yield from self.providers.values()
1✔
1125
        yield from super().related
1✔
1126

1127
    def resolve_provider_name(self, provider):
1✔
1128
        """Try to resolve provider name."""
1129
        for provider_name, container_provider in self.providers.items():
1✔
1130
            if container_provider is provider:
1✔
1131
                return provider_name
1✔
1132
        else:
1133
            raise Error(f"Can not resolve name for provider \"{provider}\"")
1✔
1134

1135
    @property
1136
    def parent(self):
1137
        """Return parent."""
1138
        return self._parent
1✔
1139

1140
    @property
1141
    def parent_name(self):
1142
        """Return parent name."""
1143
        if not self._parent:
1✔
1144
            return None
1✔
1145

1146
        name = ""
1✔
1147
        if self._parent.parent_name:
1✔
1148
            name += f"{self._parent.parent_name}."
1✔
1149
        name += f"{self._parent.resolve_provider_name(self)}"
1✔
1150

1151
        return name
1✔
1152

1153
    def assign_parent(self, parent):
1✔
1154
        """Assign parent."""
1155
        self._parent = parent
1✔
1156

1157
    def _copy_parent(self, copied, memo):
1✔
1158
        _copy_parent(self, copied, memo)
1✔
1159

1160
    cpdef object _override_providers(self, object container):
1✔
1161
        """Override providers with providers from provided container."""
1162
        for name, dependency_provider in container.providers.items():
1✔
1163
            provider = getattr(self, name)
1✔
1164

1165
            if provider.last_overriding is dependency_provider:
1✔
1166
                continue
1✔
1167

1168
            provider.override(dependency_provider)
1✔
1169

1170

1171
cdef class Callable(Provider):
1172
    r"""Callable provider calls wrapped callable on every call.
1173

1174
    Callable supports positional and keyword argument injections:
1175

1176
    .. code-block:: python
1177

1178
        some_function = Callable(some_function,
1179
                                 "positional_arg1", "positional_arg2",
1180
                                 keyword_argument1=3, keyword_argument=4)
1181

1182
        # or
1183

1184
        some_function = Callable(some_function) \
1185
            .add_args("positional_arg1", "positional_arg2") \
1186
            .add_kwargs(keyword_argument1=3, keyword_argument=4)
1187

1188
        # or
1189

1190
        some_function = Callable(some_function)
1191
        some_function.add_args("positional_arg1", "positional_arg2")
1192
        some_function.add_kwargs(keyword_argument1=3, keyword_argument=4)
1193
    """
1194

1195
    def __init__(self, provides=None, *args, **kwargs):
1196
        """Initialize provider."""
1197
        self._provides = None
1✔
1198
        self.set_provides(provides)
1✔
1199

1200
        self._args = tuple()
1✔
1201
        self._args_len = 0
1✔
1202
        self.set_args(*args)
1✔
1203

1204
        self._kwargs = tuple()
1✔
1205
        self._kwargs_len = 0
1✔
1206
        self.set_kwargs(**kwargs)
1✔
1207

1208
        super(Callable, self).__init__()
1✔
1209

1210
    def __deepcopy__(self, memo):
1✔
1211
        """Create and return full copy of provider."""
1212
        copied = memo.get(id(self))
1✔
1213
        if copied is not None:
1✔
1214
            return copied
×
1215

1216
        copied = _memorized_duplicate(self, memo)
1✔
1217
        copied.set_provides(_copy_if_provider(self.provides, memo))
1✔
1218
        copied.set_args(*deepcopy_args(self, self.args, memo))
1✔
1219
        copied.set_kwargs(**deepcopy_kwargs(self, self.kwargs, memo))
1✔
1220
        self._copy_overridings(copied, memo)
1✔
1221
        return copied
1✔
1222

1223
    def __str__(self):
1224
        """Return string representation of provider.
1225

1226
        :rtype: str
1227
        """
1228
        return represent_provider(provider=self, provides=self._provides)
1✔
1229

1230
    @property
1231
    def provides(self):
1232
        """Return provider provides."""
1233
        return self._provides
1✔
1234

1235
    def set_provides(self, provides):
1✔
1236
        """Set provider provides."""
1237
        provides = _resolve_string_import(provides)
1✔
1238
        if provides and not callable(provides):
1✔
1239
            raise Error(
1✔
1240
                "Provider {0} expected to get callable, got {1} instead".format(
1✔
1241
                    _class_qualname(self),
1✔
1242
                    provides,
1✔
1243
                ),
1244
            )
1245
        self._provides = provides
1✔
1246
        return self
1✔
1247

1248
    @property
1249
    def args(self):
1250
        """Return positional argument injections."""
1251
        cdef int index
1252
        cdef PositionalInjection arg
1253
        cdef list args
1254

1255
        args = list()
1✔
1256
        for index in range(self._args_len):
1✔
1257
            arg = self._args[index]
1✔
1258
            args.append(arg._value)
1✔
1259
        return tuple(args)
1✔
1260

1261
    def add_args(self, *args):
1✔
1262
        """Add positional argument injections.
1263

1264
        :return: Reference ``self``
1265
        """
1266
        self._args += parse_positional_injections(args)
1✔
1267
        self._args_len = len(self._args)
1✔
1268
        return self
1✔
1269

1270
    def set_args(self, *args):
1✔
1271
        """Set positional argument injections.
1272

1273
        Existing positional argument injections are dropped.
1274

1275
        :return: Reference ``self``
1276
        """
1277
        self._args = parse_positional_injections(args)
1✔
1278
        self._args_len = len(self._args)
1✔
1279
        return self
1✔
1280

1281
    def clear_args(self):
1✔
1282
        """Drop positional argument injections.
1283

1284
        :return: Reference ``self``
1285
        """
1286
        self._args = tuple()
1✔
1287
        self._args_len = len(self._args)
1✔
1288
        return self
1✔
1289

1290
    @property
1291
    def kwargs(self):
1292
        """Return keyword argument injections."""
1293
        cdef int index
1294
        cdef NamedInjection kwarg
1295
        cdef dict kwargs
1296

1297
        kwargs = dict()
1✔
1298
        for index in range(self._kwargs_len):
1✔
1299
            kwarg = self._kwargs[index]
1✔
1300
            kwargs[kwarg._name] = kwarg._value
1✔
1301
        return kwargs
1✔
1302

1303
    def add_kwargs(self, **kwargs):
1✔
1304
        """Add keyword argument injections.
1305

1306
        :return: Reference ``self``
1307
        """
1308
        self._kwargs += parse_named_injections(kwargs)
1✔
1309
        self._kwargs_len = len(self._kwargs)
1✔
1310
        return self
1✔
1311

1312
    def set_kwargs(self, **kwargs):
1✔
1313
        """Set keyword argument injections.
1314

1315
        Existing keyword argument injections are dropped.
1316

1317
        :return: Reference ``self``
1318
        """
1319
        self._kwargs = parse_named_injections(kwargs)
1✔
1320
        self._kwargs_len = len(self._kwargs)
1✔
1321
        return self
1✔
1322

1323
    def clear_kwargs(self):
1✔
1324
        """Drop keyword argument injections.
1325

1326
        :return: Reference ``self``
1327
        """
1328
        self._kwargs = tuple()
1✔
1329
        self._kwargs_len = len(self._kwargs)
1✔
1330
        return self
1✔
1331

1332
    @property
1333
    def related(self):
1334
        """Return related providers generator."""
1335
        yield from filter(is_provider, [self.provides])
1✔
1336
        yield from filter(is_provider, self.args)
1✔
1337
        yield from filter(is_provider, self.kwargs.values())
1✔
1338
        yield from super().related
1✔
1339

1340
    cpdef object _provide(self, tuple args, dict kwargs):
1✔
1341
        """Return result of provided callable call."""
1342
        return __callable_call(self, args, kwargs)
1✔
1343

1344

1345
cdef class DelegatedCallable(Callable):
1346
    """Callable that is injected "as is".
1347

1348
    DelegatedCallable is a :py:class:`Callable`, that is injected "as is".
1349
    """
1350

1351
    __IS_DELEGATED__ = True
1✔
1352

1353

1354
cdef class AbstractCallable(Callable):
1355
    """Abstract callable provider.
1356

1357
    :py:class:`AbstractCallable` is a :py:class:`Callable` provider that must
1358
    be explicitly overridden before calling.
1359

1360
    Overriding of :py:class:`AbstractCallable` is possible only by another
1361
    :py:class:`Callable` provider.
1362
    """
1363

1364
    def __call__(self, *args, **kwargs):
1365
        """Return provided object.
1366

1367
        Callable interface implementation.
1368
        """
1369
        if self._last_overriding is None:
1✔
1370
            raise Error("{0} must be overridden before calling".format(self))
1✔
1371
        return super().__call__(*args, **kwargs)
1✔
1372

1373
    def override(self, provider):
1✔
1374
        """Override provider with another provider.
1375

1376
        :param provider: Overriding provider.
1377
        :type provider: :py:class:`Provider`
1378

1379
        :raise: :py:exc:`dependency_injector.errors.Error`
1380

1381
        :return: Overriding context.
1382
        :rtype: :py:class:`OverridingContext`
1383
        """
1384
        if not isinstance(provider, Callable):
1✔
1385
            raise Error("{0} must be overridden only by "
1✔
1386
                        "{1} providers".format(self, Callable))
1✔
1387
        return super(AbstractCallable, self).override(provider)
1✔
1388

1389
    cpdef object _provide(self, tuple args, dict kwargs):
1✔
1390
        """Return result of provided callable"s call."""
1391
        raise NotImplementedError("Abstract provider forward providing logic "
1✔
1392
                                  "to overriding provider")
1393

1394

1395
cdef class CallableDelegate(Delegate):
1396
    """Callable delegate injects delegating callable "as is".
1397

1398
    .. py:attribute:: provides
1399

1400
        Value that have to be provided.
1401

1402
        :type: object
1403
    """
1404

1405
    def __init__(self, callable):
1406
        """Initializer.
1407

1408
        :param callable: Value that have to be provided.
1409
        :type callable: object
1410
        """
1411
        if isinstance(callable, Callable) is False:
1✔
1412
            raise Error("{0} can wrap only {1} providers".format(self.__class__, Callable))
1✔
1413
        super(CallableDelegate, self).__init__(callable)
1✔
1414

1415

1416
cdef class Coroutine(Callable):
1417
    r"""Coroutine provider creates wrapped coroutine on every call.
1418

1419
    Coroutine supports positional and keyword argument injections:
1420

1421
    .. code-block:: python
1422

1423
        some_coroutine = Coroutine(some_coroutine,
1424
                                   "positional_arg1", "positional_arg2",
1425
                                   keyword_argument1=3, keyword_argument=4)
1426

1427
        # or
1428

1429
        some_coroutine = Coroutine(some_coroutine) \
1430
            .add_args("positional_arg1", "positional_arg2") \
1431
            .add_kwargs(keyword_argument1=3, keyword_argument=4)
1432

1433
        # or
1434

1435
        some_coroutine = Coroutine(some_coroutine)
1436
        some_coroutine.add_args("positional_arg1", "positional_arg2")
1437
        some_coroutine.add_kwargs(keyword_argument1=3, keyword_argument=4)
1438
    """
1439

1440
    _is_coroutine_marker = _is_coroutine_marker  # Python >=3.12
1✔
1441
    _is_coroutine = _is_coroutine  # Python <3.16
1✔
1442

1443
    def set_provides(self, provides):
1✔
1444
        """Set provider provides."""
1445
        provides = _resolve_string_import(provides)
1✔
1446
        if provides and not asyncio.iscoroutinefunction(provides):
1✔
1447
            raise Error(f"Provider {_class_qualname(self)} expected to get coroutine function, "
1✔
1448
                        f"got {provides} instead")
1✔
1449
        return super().set_provides(provides)
1✔
1450

1451

1452
cdef class DelegatedCoroutine(Coroutine):
1453
    """Coroutine provider that is injected "as is".
1454

1455
    DelegatedCoroutine is a :py:class:`Coroutine`, that is injected "as is".
1456
    """
1457

1458
    __IS_DELEGATED__ = True
1✔
1459

1460

1461
cdef class AbstractCoroutine(Coroutine):
1462
    """Abstract coroutine provider.
1463

1464
    :py:class:`AbstractCoroutine` is a :py:class:`Coroutine` provider that must
1465
    be explicitly overridden before calling.
1466

1467
    Overriding of :py:class:`AbstractCoroutine` is possible only by another
1468
    :py:class:`Coroutine` provider.
1469
    """
1470

1471
    def __call__(self, *args, **kwargs):
1472
        """Return provided object.
1473

1474
        Callable interface implementation.
1475
        """
1476
        if self._last_overriding is None:
1✔
1477
            raise Error("{0} must be overridden before calling".format(self))
1✔
1478
        return super().__call__(*args, **kwargs)
×
1479

1480
    def override(self, provider):
1✔
1481
        """Override provider with another provider.
1482

1483
        :param provider: Overriding provider.
1484
        :type provider: :py:class:`Provider`
1485

1486
        :raise: :py:exc:`dependency_injector.errors.Error`
1487

1488
        :return: Overriding context.
1489
        :rtype: :py:class:`OverridingContext`
1490
        """
1491
        if not isinstance(provider, Coroutine):
1✔
1492
            raise Error("{0} must be overridden only by "
1✔
1493
                        "{1} providers".format(self, Coroutine))
1✔
1494
        return super(AbstractCoroutine, self).override(provider)
×
1495

1496
    cpdef object _provide(self, tuple args, dict kwargs):
1✔
1497
        """Return result of provided callable"s call."""
1498
        raise NotImplementedError("Abstract provider forward providing logic "
1✔
1499
                                  "to overriding provider")
1500

1501

1502
cdef class CoroutineDelegate(Delegate):
1503
    """Coroutine delegate injects delegating coroutine "as is".
1504

1505
    .. py:attribute:: provides
1506

1507
        Value that have to be provided.
1508

1509
        :type: object
1510
    """
1511

1512
    def __init__(self, coroutine):
1513
        """Initializer.
1514

1515
        :param coroutine: Value that have to be provided.
1516
        :type coroutine: object
1517
        """
1518
        if isinstance(coroutine, Coroutine) is False:
1✔
1519
            raise Error("{0} can wrap only {1} providers".format(self.__class__, Callable))
1✔
1520
        super(CoroutineDelegate, self).__init__(coroutine)
1✔
1521

1522

1523
cdef class ConfigurationOption(Provider):
1524
    """Child configuration option provider.
1525

1526
    This provider should not be used directly. It is a part of the
1527
    :py:class:`Configuration` provider.
1528
    """
1529

1530
    def __init__(self, name=None, Configuration root=None, required=False):
1531
        self._name = name
1✔
1532
        self._root = root
1✔
1533
        self._children = {}
1✔
1534
        self._required = required
1✔
1535
        self._cache = UNDEFINED
1✔
1536
        super().__init__()
1✔
1537

1538
    def __deepcopy__(self, memo):
1✔
1539
        cdef ConfigurationOption copied
1540

1541
        copied = memo.get(id(self))
1✔
1542
        if copied is not None:
1✔
1543
            return copied
×
1544

1545
        copied = <ConfigurationOption> _memorized_duplicate(self, memo)
1✔
1546
        copied._name = deepcopy(self._name, memo)
1✔
1547
        copied._root = deepcopy(self._root, memo)
1✔
1548
        copied._children = deepcopy(self._children, memo)
1✔
1549
        copied._required = self._required
1✔
1550
        self._copy_overridings(copied, memo)
1✔
1551
        return copied
1✔
1552

1553
    def __enter__(self):
1✔
1554
        return self
1✔
1555

1556
    def __exit__(self, *exc_info):
1✔
1557
        pass
1✔
1558

1559
    def __str__(self):
1560
        return represent_provider(provider=self, provides=self.get_name())
1✔
1561

1562
    def __getattr__(self, item):
1563
        if item.startswith("__") and item.endswith("__"):
1✔
1564
            raise AttributeError(
1✔
1565
                "'{cls}' object has no attribute "
1566
                "'{attribute_name}'".format(cls=self.__class__.__name__, attribute_name=item)
1✔
1567
            )
1568

1569
        child = self._children.get(item)
1✔
1570
        if child is None:
1✔
1571
            child_name = self._name + (item,)
1✔
1572
            child = ConfigurationOption(child_name, self._root)
1✔
1573
            self._children[item] = child
1✔
1574
        return child
1✔
1575

1576
    def __getitem__(self, item):
1577
        child = self._children.get(item)
1✔
1578
        if child is None:
1✔
1579
            child_name = self._name + (item,)
1✔
1580
            child = ConfigurationOption(child_name, self._root)
1✔
1581
            self._children[item] = child
1✔
1582
        return child
1✔
1583

1584
    cpdef object _provide(self, tuple args, dict kwargs):
1✔
1585
        """Return new instance."""
1586
        if self._cache is not UNDEFINED:
1✔
1587
            return self._cache
1✔
1588

1589
        value = self._root.get(self._get_self_name(), self._required)
1✔
1590
        self._cache = value
1✔
1591
        return value
1✔
1592

1593
    def _get_self_name(self):
1✔
1594
        return ".".join(
1✔
1595
            segment() if is_provider(segment) else segment for segment in self._name
1✔
1596
        )
1597

1598
    def _get_root(self):
1✔
1599
        return self._root
1✔
1600

1601
    def get_name(self):
1✔
1602
        return ".".join((self._root.get_name(), self._get_self_name()))
1✔
1603

1604
    def get_name_segments(self):
1✔
1605
        return self._name
1✔
1606

1607
    def as_int(self):
1✔
1608
        return TypedConfigurationOption(int, self)
1✔
1609

1610
    def as_float(self):
1✔
1611
        return TypedConfigurationOption(float, self)
1✔
1612

1613
    def as_(self, callback, *args, **kwargs):
1✔
1614
        return TypedConfigurationOption(callback, self, *args, **kwargs)
1✔
1615

1616
    def required(self):
1✔
1617
        return self.__class__(self._name, self._root, required=True)
1✔
1618

1619
    def is_required(self):
1✔
1620
        return self._required
1✔
1621

1622
    def override(self, value):
1✔
1623
        if isinstance(value, Provider):
1✔
1624
            raise Error("Configuration option can only be overridden by a value")
×
1625
        return self._root.set(self._get_self_name(), value)
1✔
1626

1627
    def reset_last_overriding(self):
1✔
1628
        raise Error("Configuration option does not support this method")
×
1629

1630
    def reset_override(self):
1✔
1631
        raise Error("Configuration option does not support this method")
×
1632

1633
    def reset_cache(self):
1✔
1634
        self._cache = UNDEFINED
1✔
1635

1636
        for provider in self._children.values():
1✔
1637
            provider.reset_cache()
1✔
1638

1639
        for provider in self.overrides:
1✔
1640
            if isinstance(provider, (Configuration, ConfigurationOption)):
1✔
1641
                provider.reset_cache()
1✔
1642

1643
    def update(self, value):
1✔
1644
        """Set configuration options.
1645

1646
        .. deprecated:: 3.11
1647

1648
            Use :py:meth:`Configuration.override` instead.
1649

1650
        :param value: Value of configuration option.
1651
        :type value: object | dict
1652

1653
        :rtype: None
1654
        """
1655
        self.override(value)
×
1656

1657
    def from_ini(self, filepath, required=UNDEFINED, envs_required=UNDEFINED):
1✔
1658
        """Load configuration from the ini file.
1659

1660
        Loaded configuration is merged recursively over existing configuration.
1661

1662
        :param filepath: Path to the configuration file.
1663
        :type filepath: str
1664

1665
        :param required: When required is True, raise an exception if file does not exist.
1666
        :type required: bool
1667

1668
        :param envs_required: When True, raises an error on undefined environment variable.
1669
        :type envs_required: bool
1670

1671
        :rtype: None
1672
        """
1673
        try:
1✔
1674
            parser = _parse_ini_file(
1✔
1675
                filepath,
1676
                envs_required if envs_required is not UNDEFINED else self._is_strict_mode_enabled(),
1✔
1677
            )
1678
        except IOError as exception:
1✔
1679
            if required is not False \
1✔
1680
                    and (self._is_strict_mode_enabled() or required is True) \
1✔
1681
                    and exception.errno in (errno.ENOENT, errno.EISDIR):
1✔
1682
                exception.strerror = "Unable to load configuration file {0}".format(exception.strerror)
1✔
1683
                raise
1✔
1684
            return
1✔
1685

1686
        config = {}
1✔
1687
        for section in parser.sections():
1✔
1688
            config[section] = dict(parser.items(section))
1✔
1689

1690
        current_config = self.__call__()
1✔
1691
        if not current_config:
1✔
1692
            current_config = {}
1✔
1693
        self.override(merge_dicts(current_config, config))
1✔
1694

1695
    def from_yaml(self, filepath, required=UNDEFINED, loader=None, envs_required=UNDEFINED):
1✔
1696
        """Load configuration from the yaml file.
1697

1698
        Loaded configuration is merged recursively over existing configuration.
1699

1700
        :param filepath: Path to the configuration file.
1701
        :type filepath: str
1702

1703
        :param required: When required is True, raise an exception if file does not exist.
1704
        :type required: bool
1705

1706
        :param loader: YAML loader, :py:class:`YamlLoader` is used if not specified.
1707
        :type loader: ``yaml.Loader``
1708

1709
        :param envs_required: When True, raises an error on undefined environment variable.
1710
        :type envs_required: bool
1711

1712
        :rtype: None
1713
        """
1714
        if yaml is None:
1✔
1715
            raise Error(
1✔
1716
                "Unable to load yaml configuration - PyYAML is not installed. "
1717
                "Install PyYAML or install Dependency Injector with yaml extras: "
1718
                "\"pip install dependency-injector[yaml]\""
1719
            )
1720

1721
        if loader is None:
1✔
1722
            loader = YamlLoader
1✔
1723

1724
        try:
1✔
1725
            with open(filepath) as opened_file:
1✔
1726
                config_content = opened_file.read()
1✔
1727
        except IOError as exception:
1✔
1728
            if required is not False \
1✔
1729
                    and (self._is_strict_mode_enabled() or required is True) \
1✔
1730
                    and exception.errno in (errno.ENOENT, errno.EISDIR):
1✔
1731
                exception.strerror = "Unable to load configuration file {0}".format(exception.strerror)
1✔
1732
                raise
1✔
1733
            return
1✔
1734

1735
        if envs_required is not None:
1✔
1736
            config_content = _resolve_config_env_markers(
1✔
1737
                config_content,
1✔
1738
                envs_required if envs_required is not UNDEFINED else self._is_strict_mode_enabled(),
1✔
1739
            )
1740
        config = yaml.load(config_content, loader)
1✔
1741

1742
        current_config = self.__call__()
1✔
1743
        if not current_config:
1✔
1744
            current_config = {}
1✔
1745
        self.override(merge_dicts(current_config, config))
1✔
1746

1747
    def from_json(self, filepath, required=UNDEFINED, envs_required=UNDEFINED):
1✔
1748
        """Load configuration from a json file.
1749

1750
        Loaded configuration is merged recursively over the existing configuration.
1751

1752
        :param filepath: Path to a configuration file.
1753
        :type filepath: str
1754

1755
        :param required: When required is True, raise an exception if file does not exist.
1756
        :type required: bool
1757

1758
        :param envs_required: When True, raises an exception on undefined environment variable.
1759
        :type envs_required: bool
1760

1761
        :rtype: None
1762
        """
1763
        try:
1✔
1764
            with open(filepath) as opened_file:
1✔
1765
                config_content = opened_file.read()
1✔
1766
        except IOError as exception:
1✔
1767
            if required is not False \
1✔
1768
                    and (self._is_strict_mode_enabled() or required is True) \
1✔
1769
                    and exception.errno in (errno.ENOENT, errno.EISDIR):
1✔
1770
                exception.strerror = "Unable to load configuration file {0}".format(exception.strerror)
1✔
1771
                raise
1✔
1772
            return
1✔
1773

1774
        if envs_required is not None:
1✔
1775
            config_content = _resolve_config_env_markers(
1✔
1776
                config_content,
1✔
1777
                envs_required if envs_required is not UNDEFINED else self._is_strict_mode_enabled(),
1✔
1778
            )
1779
        config = json.loads(config_content)
1✔
1780

1781
        current_config = self.__call__()
1✔
1782
        if not current_config:
1✔
1783
            current_config = {}
1✔
1784
        self.override(merge_dicts(current_config, config))
1✔
1785

1786
    def from_pydantic(self, settings, required=UNDEFINED, **kwargs):
1✔
1787
        """Load configuration from pydantic settings.
1788

1789
        Loaded configuration is merged recursively over existing configuration.
1790

1791
        :param settings: Pydantic settings instances.
1792
        :type settings: :py:class:`pydantic.BaseSettings` (pydantic v1) or
1793
            :py:class:`pydantic_settings.BaseSettings` (pydantic v2 and onwards)
1794

1795
        :param required: When required is True, raise an exception if settings dict is empty.
1796
        :type required: bool
1797

1798
        :param kwargs: Keyword arguments forwarded to ``pydantic.BaseSettings.dict()`` or
1799
            ``pydantic_settings.BaseSettings.model_dump()`` call (based on pydantic version).
1800
        :type kwargs: Dict[Any, Any]
1801

1802
        :rtype: None
1803
        """
1804

1805
        self.from_dict(pydantic_settings_to_dict(settings, kwargs), required=required)
1✔
1806

1807
    def from_dict(self, options, required=UNDEFINED):
1✔
1808
        """Load configuration from the dictionary.
1809

1810
        Loaded configuration is merged recursively over existing configuration.
1811

1812
        :param options: Configuration options.
1813
        :type options: dict
1814

1815
        :param required: When required is True, raise an exception if dictionary is empty.
1816
        :type required: bool
1817

1818
        :rtype: None
1819
        """
1820
        if required is not False \
1✔
1821
                and (self._is_strict_mode_enabled() or required is True) \
1✔
1822
                and not options:
1✔
1823
            raise ValueError("Can not use empty dictionary")
1✔
1824

1825
        try:
1✔
1826
            current_config = self.__call__()
1✔
1827
        except Error:
1✔
1828
            current_config = {}
1✔
1829
        else:
1830
            if not current_config:
1✔
1831
                current_config = {}
1✔
1832

1833
        self.override(merge_dicts(current_config, options))
1✔
1834

1835
    def from_env(self, name, default=UNDEFINED, required=UNDEFINED, as_=UNDEFINED):
1✔
1836
        """Load configuration value from the environment variable.
1837

1838
        :param name: Name of the environment variable.
1839
        :type name: str
1840

1841
        :param default: Default value that is used if environment variable does not exist.
1842
        :type default: object
1843

1844
        :param required: When required is True, raise an exception if environment variable is undefined.
1845
        :type required: bool
1846

1847
        :param as_: Callable used for type casting (int, float, etc).
1848
        :type as_: object
1849

1850
        :rtype: None
1851
        """
1852
        value = os.environ.get(name, default)
1✔
1853

1854
        if value is UNDEFINED:
1✔
1855
            if required is not False \
1✔
1856
                    and (self._is_strict_mode_enabled() or required is True):
1✔
1857
                raise ValueError("Environment variable \"{0}\" is undefined".format(name))
1✔
1858
            value = None
1✔
1859

1860
        if as_ is not UNDEFINED:
1✔
1861
            value = as_(value)
1✔
1862

1863
        self.override(value)
1✔
1864

1865
    def from_value(self, value):
1✔
1866
        """Load configuration value.
1867

1868
        :param value: Configuration value
1869
        :type value: object
1870

1871
        :rtype: None
1872
        """
1873
        self.override(value)
1✔
1874

1875
    @property
1876
    def related(self):
1877
        """Return related providers generator."""
1878
        yield from filter(is_provider, self._name)
1✔
1879
        yield from self._children.values()
1✔
1880
        yield from super().related
1✔
1881

1882
    def _is_strict_mode_enabled(self):
1✔
1883
        return self._root.__strict
1✔
1884

1885

1886
cdef class TypedConfigurationOption(Callable):
1887

1888
    @property
1889
    def option(self):
1890
        return self.args[0]
1✔
1891

1892

1893
cdef class Configuration(Object):
1894
    """Configuration provider provides configuration options to the other providers.
1895

1896
    .. code-block:: python
1897

1898
        config = Configuration("config")
1899
        print(config.section1.option1())  # None
1900
        print(config.section1.option2())  # None
1901
        config.from_dict(
1902
            {
1903
                "section1": {
1904
                    "option1": 1,
1905
                    "option2": 2,
1906
                },
1907
            },
1908
        )
1909
        print(config.section1.option1())  # 1
1910
        print(config.section1.option2())  # 2
1911
    """
1912

1913
    DEFAULT_NAME = "config"
1✔
1914

1915
    def __init__(self, name=DEFAULT_NAME, default=None, strict=False, ini_files=None, yaml_files=None, json_files=None, pydantic_settings=None):
1✔
1916
        self._name = name
1✔
1917
        self.__strict = strict
1✔
1918
        self._children = {}
1✔
1919
        self._ini_files = []
1✔
1920
        self._yaml_files = []
1✔
1921
        self._json_files = []
1✔
1922
        self._pydantic_settings = []
1✔
1923

1924
        super().__init__(provides={})
1✔
1925
        self.set_default(default)
1✔
1926

1927
        if ini_files is None:
1✔
1928
            ini_files = []
1✔
1929
        self.set_ini_files(ini_files)
1✔
1930

1931
        if yaml_files is None:
1✔
1932
            yaml_files = []
1✔
1933
        self.set_yaml_files(yaml_files)
1✔
1934

1935
        if json_files is None:
1✔
1936
            json_files = []
1✔
1937
        self.set_json_files(json_files)
1✔
1938

1939
        if pydantic_settings is None:
1✔
1940
            pydantic_settings = []
1✔
1941
        self.set_pydantic_settings(pydantic_settings)
1✔
1942

1943
    def __deepcopy__(self, memo):
1✔
1944
        copied = memo.get(id(self))
1✔
1945
        if copied is not None:
1✔
1946
            return copied
×
1947

1948
        copied = _memorized_duplicate(self, memo)
1✔
1949
        copied.set_name(self.get_name())
1✔
1950
        copied.set_default(self.get_default())
1✔
1951
        copied.set_strict(self.get_strict())
1✔
1952
        copied.set_children(deepcopy(self.get_children(), memo))
1✔
1953
        copied.set_ini_files(self.get_ini_files())
1✔
1954
        copied.set_yaml_files(self.get_yaml_files())
1✔
1955
        copied.set_json_files(self.get_json_files())
1✔
1956
        copied.set_pydantic_settings(self.get_pydantic_settings())
1✔
1957

1958
        self._copy_overridings(copied, memo)
1✔
1959
        return copied
1✔
1960

1961
    def __enter__(self):
1✔
1962
        return self
1✔
1963

1964
    def __exit__(self, *exc_info):
1✔
1965
        pass
1✔
1966

1967
    def __str__(self):
1968
        return represent_provider(provider=self, provides=self._name)
1✔
1969

1970
    def __getattr__(self, item):
1971
        if item.startswith("__") and item.endswith("__"):
1✔
1972
            raise AttributeError(
1✔
1973
                "'{cls}' object has no attribute "
1974
                "'{attribute_name}'".format(cls=self.__class__.__name__, attribute_name=item)
1✔
1975
            )
1976

1977
        child = self._children.get(item)
1✔
1978
        if child is None:
1✔
1979
            child = ConfigurationOption((item,), self)
1✔
1980
            self._children[item] = child
1✔
1981
        return child
1✔
1982

1983
    def __getitem__(self, item):
1984
        child = self._children.get(item)
×
1985
        if child is None:
×
1986
            child = ConfigurationOption(item, self)
×
1987
            self._children[item] = child
×
1988
        return child
×
1989

1990
    def get_name(self):
1✔
1991
        """Return name."""
1992
        return self._name
1✔
1993

1994
    def set_name(self, name):
1✔
1995
        """Set name."""
1996
        self._name = name
1✔
1997
        return self
1✔
1998

1999
    def get_default(self):
1✔
2000
        """Return default."""
2001
        return self.provides
1✔
2002

2003
    def set_default(self, default):
1✔
2004
        """Set default."""
2005
        if not default:
1✔
2006
            return self
1✔
2007

2008
        assert isinstance(default, dict), default
1✔
2009
        self.set_provides(default.copy())
1✔
2010
        return self
1✔
2011

2012
    def get_strict(self):
1✔
2013
        """Return strict flag."""
2014
        return self.__strict
1✔
2015

2016
    def set_strict(self, strict):
1✔
2017
        """Set strict flag."""
2018
        self.__strict = strict
1✔
2019
        return self
1✔
2020

2021
    def get_children(self):
1✔
2022
        """Return children options."""
2023
        return self._children
1✔
2024

2025
    def set_children(self, children):
1✔
2026
        """Set children options."""
2027
        self._children = children
1✔
2028
        return self
1✔
2029

2030
    def get_ini_files(self):
1✔
2031
        """Return list of INI files."""
2032
        return list(self._ini_files)
1✔
2033

2034
    def set_ini_files(self, files):
1✔
2035
        """Set list of INI files."""
2036
        self._ini_files = list(files)
1✔
2037
        return self
1✔
2038

2039
    def get_yaml_files(self):
1✔
2040
        """Return list of YAML files."""
2041
        return list(self._yaml_files)
1✔
2042

2043
    def set_yaml_files(self, files):
1✔
2044
        """Set list of YAML files."""
2045
        self._yaml_files = list(files)
1✔
2046
        return self
1✔
2047

2048
    def get_json_files(self):
1✔
2049
        """Return list of JSON files."""
2050
        return list(self._json_files)
1✔
2051

2052
    def set_json_files(self, files):
1✔
2053
        """Set list of JSON files."""
2054
        self._json_files = list(files)
1✔
2055
        return self
1✔
2056

2057
    def get_pydantic_settings(self):
1✔
2058
        """Return list of Pydantic settings."""
2059
        return list(self._pydantic_settings)
1✔
2060

2061
    def set_pydantic_settings(self, settings):
1✔
2062
        """Set list of Pydantic settings."""
2063
        self._pydantic_settings = list(settings)
1✔
2064
        return self
1✔
2065

2066
    def load(self, required=UNDEFINED, envs_required=UNDEFINED):
1✔
2067
        """Load configuration.
2068

2069
        This method loads configuration from configuration files or pydantic settings that
2070
        were set earlier with set_*() methods or provided to the __init__(), e.g.:
2071

2072
        .. code-block:: python
2073

2074
           config = providers.Configuration(yaml_files=[file1, file2])
2075
           config.load()
2076

2077
        :param required: When required is True, raise an exception if file does not exist.
2078
        :type required: bool
2079

2080
        :param envs_required: When True, raises an error on undefined environment variable.
2081
        :type envs_required: bool
2082
        """
2083
        for file in self.get_ini_files():
1✔
2084
            self.from_ini(file, required=required, envs_required=envs_required)
1✔
2085

2086
        for file in self.get_yaml_files():
1✔
2087
            self.from_yaml(file, required=required, envs_required=envs_required)
1✔
2088

2089
        for file in self.get_json_files():
1✔
2090
            self.from_json(file, required=required, envs_required=envs_required)
1✔
2091

2092
        for settings in self.get_pydantic_settings():
1✔
2093
            self.from_pydantic(settings, required=required)
1✔
2094

2095
    def get(self, selector, required=False):
1✔
2096
        """Return configuration option.
2097

2098
        :param selector: Selector string, e.g. "option1.option2"
2099
        :type selector: str
2100

2101
        :param required: Required flag, raise error if required option is missing
2102
        :type required: bool
2103

2104
        :return: Option value.
2105
        :rtype: Any
2106
        """
2107
        value = self.__call__()
1✔
2108

2109
        if value is None:
1✔
2110
            if self._is_strict_mode_enabled() or required:
1✔
2111
                raise Error("Undefined configuration option \"{0}.{1}\"".format(self._name, selector))
1✔
2112
            return None
1✔
2113

2114
        keys = selector.split(".")
1✔
2115
        while len(keys) > 0:
1✔
2116
            key = keys.pop(0)
1✔
2117
            value = value.get(key, UNDEFINED)
1✔
2118

2119
            if value is UNDEFINED:
1✔
2120
                if self._is_strict_mode_enabled() or required:
1✔
2121
                    raise Error("Undefined configuration option \"{0}.{1}\"".format(self._name, selector))
1✔
2122
                return None
1✔
2123

2124
        return value
1✔
2125

2126
    def set(self, selector, value):
1✔
2127
        """Override configuration option.
2128

2129
        :param selector: Selector string, e.g. "option1.option2"
2130
        :type selector: str
2131

2132
        :param value: Overriding value
2133
        :type value: Any
2134

2135
        :return: Overriding context.
2136
        :rtype: :py:class:`OverridingContext`
2137
        """
2138
        original_value = current_value = deepcopy(self.__call__())
1✔
2139

2140
        keys = selector.split(".")
1✔
2141
        while len(keys) > 0:
1✔
2142
            key = keys.pop(0)
1✔
2143
            if len(keys) == 0:
1✔
2144
                current_value[key] = value
1✔
2145
                break
1✔
2146
            temp_value = current_value.get(key, {})
1✔
2147
            current_value[key] = temp_value
1✔
2148
            current_value = temp_value
1✔
2149

2150
        return self.override(original_value)
1✔
2151

2152
    def override(self, provider):
1✔
2153
        """Override provider with another provider.
2154

2155
        :param provider: Overriding provider.
2156
        :type provider: :py:class:`Provider`
2157

2158
        :raise: :py:exc:`dependency_injector.errors.Error`
2159

2160
        :return: Overriding context.
2161
        :rtype: :py:class:`OverridingContext`
2162
        """
2163
        context = super().override(provider)
1✔
2164
        self.reset_cache()
1✔
2165
        return context
1✔
2166

2167
    def reset_last_overriding(self):
1✔
2168
        """Reset last overriding provider.
2169

2170
        :raise: :py:exc:`dependency_injector.errors.Error` if provider is not
2171
                overridden.
2172

2173
        :rtype: None
2174
        """
2175
        super().reset_last_overriding()
1✔
2176
        self.reset_cache()
1✔
2177

2178
    def reset_override(self):
1✔
2179
        """Reset all overriding providers.
2180

2181
        :rtype: None
2182
        """
2183
        super().reset_override()
1✔
2184
        self.reset_cache()
1✔
2185

2186
    def reset_cache(self):
1✔
2187
        """Reset children providers cache.
2188

2189
        :rtype: None
2190
        """
2191
        for provider in self._children.values():
1✔
2192
            provider.reset_cache()
1✔
2193

2194
        for provider in self.overrides:
1✔
2195
            if isinstance(provider, (Configuration, ConfigurationOption)):
1✔
2196
                provider.reset_cache()
1✔
2197

2198
    def update(self, value):
1✔
2199
        """Set configuration options.
2200

2201
        .. deprecated:: 3.11
2202

2203
            Use :py:meth:`Configuration.override` instead.
2204

2205
        :param value: Value of configuration option.
2206
        :type value: object | dict
2207

2208
        :rtype: None
2209
        """
2210
        self.override(value)
1✔
2211

2212
    def from_ini(self, filepath, required=UNDEFINED, envs_required=UNDEFINED):
1✔
2213
        """Load configuration from the ini file.
2214

2215
        Loaded configuration is merged recursively over existing configuration.
2216

2217
        :param filepath: Path to the configuration file.
2218
        :type filepath: str
2219

2220
        :param required: When required is True, raise an exception if file does not exist.
2221
        :type required: bool
2222

2223
        :param envs_required: When True, raises an error on undefined environment variable.
2224
        :type envs_required: bool
2225

2226
        :rtype: None
2227
        """
2228
        try:
1✔
2229
            parser = _parse_ini_file(
1✔
2230
                filepath,
2231
                envs_required if envs_required is not UNDEFINED else self._is_strict_mode_enabled(),
1✔
2232
            )
2233
        except IOError as exception:
1✔
2234
            if required is not False \
1✔
2235
                    and (self._is_strict_mode_enabled() or required is True) \
1✔
2236
                    and exception.errno in (errno.ENOENT, errno.EISDIR):
1✔
2237
                exception.strerror = "Unable to load configuration file {0}".format(exception.strerror)
1✔
2238
                raise
1✔
2239
            return
1✔
2240

2241
        config = {}
1✔
2242
        for section in parser.sections():
1✔
2243
            config[section] = dict(parser.items(section))
1✔
2244

2245
        current_config = self.__call__()
1✔
2246
        if not current_config:
1✔
2247
            current_config = {}
1✔
2248
        self.override(merge_dicts(current_config, config))
1✔
2249

2250
    def from_yaml(self, filepath, required=UNDEFINED, loader=None, envs_required=UNDEFINED):
1✔
2251
        """Load configuration from the yaml file.
2252

2253
        Loaded configuration is merged recursively over existing configuration.
2254

2255
        :param filepath: Path to the configuration file.
2256
        :type filepath: str
2257

2258
        :param required: When required is True, raise an exception if file does not exist.
2259
        :type required: bool
2260

2261
        :param loader: YAML loader, :py:class:`YamlLoader` is used if not specified.
2262
        :type loader: ``yaml.Loader``
2263

2264
        :param envs_required: When True, raises an error on undefined environment variable.
2265
        :type envs_required: bool
2266

2267
        :rtype: None
2268
        """
2269
        if yaml is None:
1✔
2270
            raise Error(
1✔
2271
                "Unable to load yaml configuration - PyYAML is not installed. "
2272
                "Install PyYAML or install Dependency Injector with yaml extras: "
2273
                "\"pip install dependency-injector[yaml]\""
2274
            )
2275

2276
        if loader is None:
1✔
2277
            loader = YamlLoader
1✔
2278

2279
        try:
1✔
2280
            with open(filepath) as opened_file:
1✔
2281
                config_content = opened_file.read()
1✔
2282
        except IOError as exception:
1✔
2283
            if required is not False \
1✔
2284
                    and (self._is_strict_mode_enabled() or required is True) \
1✔
2285
                    and exception.errno in (errno.ENOENT, errno.EISDIR):
1✔
2286
                exception.strerror = "Unable to load configuration file {0}".format(exception.strerror)
1✔
2287
                raise
1✔
2288
            return
1✔
2289

2290
        if envs_required is not None:
1✔
2291
            config_content = _resolve_config_env_markers(
1✔
2292
                config_content,
1✔
2293
                envs_required if envs_required is not UNDEFINED else self._is_strict_mode_enabled(),
1✔
2294
            )
2295
        config = yaml.load(config_content, loader)
1✔
2296

2297
        current_config = self.__call__()
1✔
2298
        if not current_config:
1✔
2299
            current_config = {}
1✔
2300
        self.override(merge_dicts(current_config, config))
1✔
2301

2302
    def from_json(self, filepath, required=UNDEFINED, envs_required=UNDEFINED):
1✔
2303
        """Load configuration from a json file.
2304

2305
        Loaded configuration is merged recursively over the existing configuration.
2306

2307
        :param filepath: Path to a configuration file.
2308
        :type filepath: str
2309

2310
        :param required: When required is True, raise an exception if file does not exist.
2311
        :type required: bool
2312

2313
        :param envs_required: When True, raises an exception on undefined environment variable.
2314
        :type envs_required: bool
2315

2316
        :rtype: None
2317
        """
2318
        try:
1✔
2319
            with open(filepath) as opened_file:
1✔
2320
                config_content = opened_file.read()
1✔
2321
        except IOError as exception:
1✔
2322
            if required is not False \
1✔
2323
                    and (self._is_strict_mode_enabled() or required is True) \
1✔
2324
                    and exception.errno in (errno.ENOENT, errno.EISDIR):
1✔
2325
                exception.strerror = "Unable to load configuration file {0}".format(exception.strerror)
1✔
2326
                raise
1✔
2327
            return
1✔
2328

2329
        if envs_required is not None:
1✔
2330
            config_content = _resolve_config_env_markers(
1✔
2331
                config_content,
1✔
2332
                envs_required if envs_required is not UNDEFINED else self._is_strict_mode_enabled(),
1✔
2333
            )
2334
        config = json.loads(config_content)
1✔
2335

2336
        current_config = self.__call__()
1✔
2337
        if not current_config:
1✔
2338
            current_config = {}
1✔
2339
        self.override(merge_dicts(current_config, config))
1✔
2340

2341
    def from_pydantic(self, settings, required=UNDEFINED, **kwargs):
1✔
2342
        """Load configuration from pydantic settings.
2343

2344
        Loaded configuration is merged recursively over existing configuration.
2345

2346
        :param settings: Pydantic settings instances.
2347
        :type settings: :py:class:`pydantic.BaseSettings` (pydantic v1) or
2348
            :py:class:`pydantic_settings.BaseSettings` (pydantic v2 and onwards)
2349

2350
        :param required: When required is True, raise an exception if settings dict is empty.
2351
        :type required: bool
2352

2353
        :param kwargs: Keyword arguments forwarded to ``pydantic.BaseSettings.dict()`` call.
2354
        :type kwargs: Dict[Any, Any]
2355

2356
        :rtype: None
2357
        """
2358

2359
        self.from_dict(pydantic_settings_to_dict(settings, kwargs), required=required)
1✔
2360

2361
    def from_dict(self, options, required=UNDEFINED):
1✔
2362
        """Load configuration from the dictionary.
2363

2364
        Loaded configuration is merged recursively over existing configuration.
2365

2366
        :param options: Configuration options.
2367
        :type options: dict
2368

2369
        :param required: When required is True, raise an exception if dictionary is empty.
2370
        :type required: bool
2371

2372
        :rtype: None
2373
        """
2374
        if required is not False \
1✔
2375
                and (self._is_strict_mode_enabled() or required is True) \
1✔
2376
                and not options:
1✔
2377
            raise ValueError("Can not use empty dictionary")
1✔
2378

2379
        current_config = self.__call__()
1✔
2380
        if not current_config:
1✔
2381
            current_config = {}
1✔
2382
        self.override(merge_dicts(current_config, options))
1✔
2383

2384
    def from_env(self, name, default=UNDEFINED, required=UNDEFINED, as_=UNDEFINED):
1✔
2385
        """Load configuration value from the environment variable.
2386

2387
        :param name: Name of the environment variable.
2388
        :type name: str
2389

2390
        :param default: Default value that is used if environment variable does not exist.
2391
        :type default: object
2392

2393
        :param required: When required is True, raise an exception if environment variable is undefined.
2394
        :type required: bool
2395

2396
        :param as_: Callable used for type casting (int, float, etc).
2397
        :type as_: object
2398

2399
        :rtype: None
2400
        """
2401
        value = os.environ.get(name, default)
1✔
2402

2403
        if value is UNDEFINED:
1✔
2404
            if required is not False \
1✔
2405
                    and (self._is_strict_mode_enabled() or required is True):
1✔
2406
                raise ValueError("Environment variable \"{0}\" is undefined".format(name))
1✔
2407
            value = None
1✔
2408

2409
        if as_ is not UNDEFINED:
1✔
2410
            value = as_(value)
1✔
2411

2412
        self.override(value)
1✔
2413

2414
    def from_value(self, value):
1✔
2415
        """Load configuration value.
2416

2417
        :param value: Configuration value
2418
        :type value: object
2419

2420
        :rtype: None
2421
        """
2422
        self.override(value)
1✔
2423

2424
    @property
2425
    def related(self):
2426
        """Return related providers generator."""
2427
        yield from self._children.values()
1✔
2428
        yield from super().related
1✔
2429

2430
    def _is_strict_mode_enabled(self):
1✔
2431
        return self.__strict
1✔
2432

2433

2434
cdef class Factory(Provider):
2435
    r"""Factory provider creates new instance on every call.
2436

2437
    :py:class:`Factory` supports positional & keyword argument injections,
2438
    as well as attribute injections.
2439

2440
    Positional and keyword argument injections could be defined like this:
2441

2442
    .. code-block:: python
2443

2444
        factory = Factory(SomeClass,
2445
                          "positional_arg1", "positional_arg2",
2446
                          keyword_argument1=3, keyword_argument=4)
2447

2448
        # or
2449

2450
        factory = Factory(SomeClass) \
2451
            .add_args("positional_arg1", "positional_arg2") \
2452
            .add_kwargs(keyword_argument1=3, keyword_argument=4)
2453

2454
        # or
2455

2456
        factory = Factory(SomeClass)
2457
        factory.add_args("positional_arg1", "positional_arg2")
2458
        factory.add_kwargs(keyword_argument1=3, keyword_argument=4)
2459

2460

2461
    Attribute injections are defined by using
2462
    :py:meth:`Factory.add_attributes`:
2463

2464
    .. code-block:: python
2465

2466
        factory = Factory(SomeClass) \
2467
            .add_attributes(attribute1=1, attribute2=2)
2468

2469
    Retrieving of provided instance can be performed via calling
2470
    :py:class:`Factory` object:
2471

2472
    .. code-block:: python
2473

2474
        factory = Factory(SomeClass)
2475
        some_object = factory()
2476

2477
    .. py:attribute:: provided_type
2478

2479
        If provided type is defined, provider checks that providing class is
2480
        its subclass.
2481

2482
        :type: type | None
2483
    """
2484

2485
    provided_type = None
1✔
2486

2487
    def __init__(self, provides=None, *args, **kwargs):
2488
        """Initialize provider."""
2489
        self._instantiator = Callable()
1✔
2490
        self.set_provides(provides)
1✔
2491
        self.set_args(*args)
1✔
2492
        self.set_kwargs(**kwargs)
1✔
2493

2494
        self._attributes = tuple()
1✔
2495
        self._attributes_len = 0
1✔
2496

2497
        super(Factory, self).__init__()
1✔
2498

2499
    def __deepcopy__(self, memo):
1✔
2500
        """Create and return full copy of provider."""
2501
        copied = memo.get(id(self))
1✔
2502
        if copied is not None:
1✔
2503
            return copied
×
2504

2505
        copied = _memorized_duplicate(self, memo)
1✔
2506
        copied.set_provides(_copy_if_provider(self.provides, memo))
1✔
2507
        copied.set_args(*deepcopy_args(self, self.args, memo))
1✔
2508
        copied.set_kwargs(**deepcopy_kwargs(self, self.kwargs, memo))
1✔
2509
        copied.set_attributes(**deepcopy(self.attributes, memo))
1✔
2510
        self._copy_overridings(copied, memo)
1✔
2511
        return copied
1✔
2512

2513
    def __str__(self):
2514
        """Return string representation of provider.
2515

2516
        :rtype: str
2517
        """
2518
        return represent_provider(provider=self,
1✔
2519
                                  provides=self._instantiator.provides)
1✔
2520

2521
    @property
2522
    def cls(self):
2523
        """Return provided type."""
2524
        return self.provides
1✔
2525

2526
    @property
2527
    def provides(self):
2528
        """Return provider provides."""
2529
        return self._instantiator.provides
1✔
2530

2531
    def set_provides(self, provides):
1✔
2532
        """Set provider provides."""
2533
        provides = _resolve_string_import(provides)
1✔
2534
        if (provides
1✔
2535
                and self.__class__.provided_type and
1✔
2536
                not issubclass(provides, self.__class__.provided_type)):
1✔
2537
            raise Error(
1✔
2538
                "{0} can provide only {1} instances".format(
1✔
2539
                    _class_qualname(self),
1✔
2540
                    self.__class__.provided_type,
1✔
2541
                ),
2542
            )
2543
        self._instantiator.set_provides(provides)
1✔
2544
        return self
1✔
2545

2546
    @property
2547
    def args(self):
2548
        """Return positional argument injections."""
2549
        return self._instantiator.args
1✔
2550

2551
    def add_args(self, *args):
1✔
2552
        """Add __init__ positional argument injections.
2553

2554
        :return: Reference ``self``
2555
        """
2556
        self._instantiator.add_args(*args)
1✔
2557
        return self
1✔
2558

2559
    def set_args(self, *args):
1✔
2560
        """Set __init__ positional argument injections.
2561

2562
        Existing __init__ positional argument injections are dropped.
2563

2564
        :return: Reference ``self``
2565
        """
2566
        self._instantiator.set_args(*args)
1✔
2567
        return self
1✔
2568

2569
    def clear_args(self):
1✔
2570
        """Drop __init__ positional argument injections.
2571

2572
        :return: Reference ``self``
2573
        """
2574
        self._instantiator.clear_args()
1✔
2575
        return self
1✔
2576

2577
    @property
2578
    def kwargs(self):
2579
        """Return keyword argument injections."""
2580
        return self._instantiator.kwargs
1✔
2581

2582
    def add_kwargs(self, **kwargs):
1✔
2583
        """Add __init__ keyword argument injections.
2584

2585
        :return: Reference ``self``
2586
        """
2587
        self._instantiator.add_kwargs(**kwargs)
1✔
2588
        return self
1✔
2589

2590
    def set_kwargs(self, **kwargs):
1✔
2591
        """Set __init__ keyword argument injections.
2592

2593
        Existing __init__ keyword argument injections are dropped.
2594

2595
        :return: Reference ``self``
2596
        """
2597
        self._instantiator.set_kwargs(**kwargs)
1✔
2598
        return self
1✔
2599

2600
    def clear_kwargs(self):
1✔
2601
        """Drop __init__ keyword argument injections.
2602

2603
        :return: Reference ``self``
2604
        """
2605
        self._instantiator.clear_kwargs()
1✔
2606
        return self
1✔
2607

2608
    @property
2609
    def attributes(self):
2610
        """Return attribute injections."""
2611
        cdef int index
2612
        cdef NamedInjection attribute
2613
        cdef dict attributes
2614

2615
        attributes = dict()
1✔
2616
        for index in range(self._attributes_len):
1✔
2617
            attribute = self._attributes[index]
1✔
2618
            attributes[attribute._name] = attribute._value
1✔
2619
        return attributes
1✔
2620

2621
    def add_attributes(self, **kwargs):
1✔
2622
        """Add attribute injections.
2623

2624
        :return: Reference ``self``
2625
        """
2626
        self._attributes += parse_named_injections(kwargs)
1✔
2627
        self._attributes_len = len(self._attributes)
1✔
2628
        return self
1✔
2629

2630
    def set_attributes(self, **kwargs):
1✔
2631
        """Set attribute injections.
2632

2633
        Existing attribute injections are dropped.
2634

2635
        :return: Reference ``self``
2636
        """
2637
        self._attributes = parse_named_injections(kwargs)
1✔
2638
        self._attributes_len = len(self._attributes)
1✔
2639
        return self
1✔
2640

2641
    def clear_attributes(self):
1✔
2642
        """Drop attribute injections.
2643

2644
        :return: Reference ``self``
2645
        """
2646
        self._attributes = tuple()
1✔
2647
        self._attributes_len = len(self._attributes)
1✔
2648
        return self
1✔
2649

2650
    @property
2651
    def related(self):
2652
        """Return related providers generator."""
2653
        yield from filter(is_provider, [self.provides])
1✔
2654
        yield from filter(is_provider, self.args)
1✔
2655
        yield from filter(is_provider, self.kwargs.values())
1✔
2656
        yield from filter(is_provider, self.attributes.values())
1✔
2657
        yield from super().related
1✔
2658

2659
    cpdef object _provide(self, tuple args, dict kwargs):
1✔
2660
        """Return new instance."""
2661
        return __factory_call(self, args, kwargs)
1✔
2662

2663

2664
cdef class DelegatedFactory(Factory):
2665
    """Factory that is injected "as is".
2666

2667
    .. py:attribute:: provided_type
2668

2669
        If provided type is defined, provider checks that providing class is
2670
        its subclass.
2671

2672
        :type: type | None
2673

2674
    .. py:attribute:: cls
2675
       :noindex:
2676

2677
        Class that provides object.
2678
        Alias for :py:attr:`provides`.
2679

2680
        :type: type
2681
    """
2682

2683
    __IS_DELEGATED__ = True
1✔
2684

2685

2686
cdef class AbstractFactory(Factory):
2687
    """Abstract factory provider.
2688

2689
    :py:class:`AbstractFactory` is a :py:class:`Factory` provider that must
2690
    be explicitly overridden before calling.
2691

2692
    Overriding of :py:class:`AbstractFactory` is possible only by another
2693
    :py:class:`Factory` provider.
2694
    """
2695

2696
    def __call__(self, *args, **kwargs):
2697
        """Return provided object.
2698

2699
        Callable interface implementation.
2700
        """
2701
        if self._last_overriding is None:
1✔
2702
            raise Error("{0} must be overridden before calling".format(self))
1✔
2703
        return super().__call__(*args, **kwargs)
1✔
2704

2705
    def override(self, provider):
1✔
2706
        """Override provider with another provider.
2707

2708
        :param provider: Overriding provider.
2709
        :type provider: :py:class:`Provider`
2710

2711
        :raise: :py:exc:`dependency_injector.errors.Error`
2712

2713
        :return: Overriding context.
2714
        :rtype: :py:class:`OverridingContext`
2715
        """
2716
        if not isinstance(provider, Factory):
1✔
2717
            raise Error("{0} must be overridden only by "
1✔
2718
                        "{1} providers".format(self, Factory))
1✔
2719
        return super(AbstractFactory, self).override(provider)
1✔
2720

2721
    cpdef object _provide(self, tuple args, dict kwargs):
1✔
2722
        """Return result of provided callable call."""
2723
        raise NotImplementedError("Abstract provider forward providing logic to overriding provider")
1✔
2724

2725

2726
cdef class FactoryDelegate(Delegate):
2727
    """Factory delegate injects delegating factory "as is".
2728

2729
    .. py:attribute:: provides
2730

2731
        Value that have to be provided.
2732

2733
        :type: object
2734
    """
2735

2736
    def __init__(self, factory):
2737
        """Initializer.
2738

2739
        :param factory: Value that have to be provided.
2740
        :type factory: object
2741
        """
2742
        if isinstance(factory, Factory) is False:
1✔
2743
            raise Error("{0} can wrap only {1} providers".format(self.__class__, Factory))
1✔
2744
        super(FactoryDelegate, self).__init__(factory)
1✔
2745

2746

2747
cdef class FactoryAggregate(Aggregate):
2748
    """Factory providers aggregate.
2749

2750
    :py:class:`FactoryAggregate` is an aggregate of :py:class:`Factory`
2751
    providers.
2752

2753
    :py:class:`FactoryAggregate` is a delegated provider, meaning that it is
2754
    injected "as is".
2755

2756
    All aggregated providers can be retrieved as a read-only
2757
    dictionary :py:attr:`FactoryAggregate.providers` or as an attribute of
2758
    :py:class:`FactoryAggregate`.
2759
    """
2760

2761
    @property
2762
    def factories(self):
2763
        """Return dictionary of factories, read-only.
2764

2765
        Alias for ``.providers()`` attribute.
2766
        """
2767
        return self.providers
1✔
2768

2769
    def set_factories(self, factory_dict=None, **factory_kwargs):
1✔
2770
        """Set factories.
2771

2772
        Alias for ``.set_providers()`` method.
2773
        """
2774
        return self.set_providers(factory_dict, **factory_kwargs)
1✔
2775

2776

2777
cdef class BaseSingleton(Provider):
2778
    """Base class of singleton providers."""
2779

2780
    provided_type = None
1✔
2781

2782
    def __init__(self, provides=None, *args, **kwargs):
2783
        """Initialize provider."""
2784
        self._instantiator = Factory()
1✔
2785
        self.set_provides(provides)
1✔
2786
        self.set_args(*args)
1✔
2787
        self.set_kwargs(**kwargs)
1✔
2788
        super(BaseSingleton, self).__init__()
1✔
2789

2790
    def __str__(self):
2791
        """Return string representation of provider.
2792

2793
        :rtype: str
2794
        """
2795
        return represent_provider(provider=self,
1✔
2796
                                  provides=self._instantiator.cls)
1✔
2797

2798
    def __deepcopy__(self, memo):
1✔
2799
        """Create and return full copy of provider."""
2800
        copied = memo.get(id(self))
1✔
2801
        if copied is not None:
1✔
2802
            return copied
×
2803

2804
        copied = _memorized_duplicate(self, memo)
1✔
2805
        copied.set_provides(_copy_if_provider(self.provides, memo))
1✔
2806
        copied.set_args(*deepcopy_args(self, self.args, memo))
1✔
2807
        copied.set_kwargs(**deepcopy_kwargs(self, self.kwargs, memo))
1✔
2808
        copied.set_attributes(**deepcopy(self.attributes, memo))
1✔
2809
        self._copy_overridings(copied, memo)
1✔
2810
        return copied
1✔
2811

2812
    @property
2813
    def cls(self):
2814
        """Return provided type."""
2815
        return self.provides
1✔
2816

2817
    @property
2818
    def provides(self):
2819
        """Return provider provides."""
2820
        return self._instantiator.provides
1✔
2821

2822
    def set_provides(self, provides):
1✔
2823
        """Set provider provides."""
2824
        provides = _resolve_string_import(provides)
1✔
2825
        if (provides
1✔
2826
                and self.__class__.provided_type and
1✔
2827
                not issubclass(provides, self.__class__.provided_type)):
1✔
2828
            raise Error(
1✔
2829
                "{0} can provide only {1} instances".format(
1✔
2830
                    _class_qualname(self),
1✔
2831
                    self.__class__.provided_type,
1✔
2832
                ),
2833
            )
2834
        self._instantiator.set_provides(provides)
1✔
2835
        return self
1✔
2836

2837
    @property
2838
    def args(self):
2839
        """Return positional argument injections."""
2840
        return self._instantiator.args
1✔
2841

2842
    def add_args(self, *args):
1✔
2843
        """Add __init__ positional argument injections.
2844

2845
        :return: Reference ``self``
2846
        """
2847
        self._instantiator.add_args(*args)
1✔
2848
        return self
1✔
2849

2850
    def set_args(self, *args):
1✔
2851
        """Set __init__ positional argument injections.
2852

2853
        Existing __init__ positional argument injections are dropped.
2854

2855
        :return: Reference ``self``
2856
        """
2857
        self._instantiator.set_args(*args)
1✔
2858
        return self
1✔
2859

2860
    def clear_args(self):
1✔
2861
        """Drop __init__ positional argument injections.
2862

2863
        :return: Reference ``self``
2864
        """
2865
        self._instantiator.clear_args()
1✔
2866
        return self
1✔
2867

2868
    @property
2869
    def kwargs(self):
2870
        """Return keyword argument injections."""
2871
        return self._instantiator.kwargs
1✔
2872

2873
    def add_kwargs(self, **kwargs):
1✔
2874
        """Add __init__ keyword argument injections.
2875

2876
        :return: Reference ``self``
2877
        """
2878
        self._instantiator.add_kwargs(**kwargs)
1✔
2879
        return self
1✔
2880

2881
    def set_kwargs(self, **kwargs):
1✔
2882
        """Set __init__ keyword argument injections.
2883

2884
        Existing __init__ keyword argument injections are dropped.
2885

2886
        :return: Reference ``self``
2887
        """
2888
        self._instantiator.set_kwargs(**kwargs)
1✔
2889
        return self
1✔
2890

2891
    def clear_kwargs(self):
1✔
2892
        """Drop __init__ keyword argument injections.
2893

2894
        :return: Reference ``self``
2895
        """
2896
        self._instantiator.clear_kwargs()
1✔
2897
        return self
1✔
2898

2899
    @property
2900
    def attributes(self):
2901
        """Return attribute injections."""
2902
        return self._instantiator.attributes
1✔
2903

2904
    def add_attributes(self, **kwargs):
1✔
2905
        """Add attribute injections.
2906

2907
        :return: Reference ``self``
2908
        """
2909
        self._instantiator.add_attributes(**kwargs)
1✔
2910
        return self
1✔
2911

2912
    def set_attributes(self, **kwargs):
1✔
2913
        """Set attribute injections.
2914

2915
        Existing attribute injections are dropped.
2916

2917
        :return: Reference ``self``
2918
        """
2919
        self._instantiator.set_attributes(**kwargs)
1✔
2920
        return self
1✔
2921

2922
    def clear_attributes(self):
1✔
2923
        """Drop attribute injections.
2924

2925
        :return: Reference ``self``
2926
        """
2927
        self._instantiator.clear_attributes()
1✔
2928
        return self
1✔
2929

2930
    def reset(self):
1✔
2931
        """Reset cached instance, if any.
2932

2933
        :rtype: None
2934
        """
2935
        raise NotImplementedError()
×
2936

2937
    def full_reset(self):
1✔
2938
        """Reset cached instance in current and all underlying singletons, if any.
2939

2940
        :rtype: :py:class:`SingletonFullResetContext`
2941
        """
2942
        self.reset()
1✔
2943
        for provider in self.traverse(types=[BaseSingleton]):
1✔
2944
            provider.reset()
1✔
2945
        return SingletonFullResetContext(self)
1✔
2946

2947
    @property
2948
    def related(self):
2949
        """Return related providers generator."""
2950
        yield from filter(is_provider, [self._instantiator.provides])
1✔
2951
        yield from filter(is_provider, self.args)
1✔
2952
        yield from filter(is_provider, self.kwargs.values())
1✔
2953
        yield from filter(is_provider, self.attributes.values())
1✔
2954
        yield from super().related
1✔
2955

2956
    def _async_init_instance(self, future_result, result):
1✔
2957
        try:
1✔
2958
            instance = result.result()
1✔
2959
        except Exception as exception:
1✔
2960
            self._storage = None
1✔
2961
            future_result.set_exception(exception)
1✔
2962
        else:
2963
            self._storage = instance
1✔
2964
            future_result.set_result(instance)
1✔
2965

2966

2967
cdef class Singleton(BaseSingleton):
2968
    """Singleton provider returns same instance on every call.
2969

2970
    :py:class:`Singleton` provider creates instance once and returns it on
2971
    every call. :py:class:`Singleton` extends :py:class:`Factory`, so, please
2972
    follow :py:class:`Factory` documentation for getting familiar with
2973
    injections syntax.
2974

2975
    Retrieving of provided instance can be performed via calling
2976
    :py:class:`Singleton` object:
2977

2978
    .. code-block:: python
2979

2980
        singleton = Singleton(SomeClass)
2981
        some_object = singleton()
2982

2983
    .. py:attribute:: provided_type
2984

2985
        If provided type is defined, provider checks that providing class is
2986
        its subclass.
2987

2988
        :type: type | None
2989

2990
    .. py:attribute:: cls
2991
       :noindex:
2992

2993
        Class that provides object.
2994
        Alias for :py:attr:`provides`.
2995

2996
        :type: type
2997
    """
2998

2999
    def __init__(self, provides=None, *args, **kwargs):
3000
        """Initializer.
3001

3002
        :param provides: Provided type.
3003
        :type provides: type
3004
        """
3005
        self._storage = None
1✔
3006
        super(Singleton, self).__init__(provides, *args, **kwargs)
1✔
3007

3008
    def reset(self):
1✔
3009
        """Reset cached instance, if any.
3010

3011
        :rtype: None
3012
        """
3013
        if __is_future_or_coroutine(self._storage):
1✔
3014
            asyncio.ensure_future(self._storage).cancel()
×
3015
        self._storage = None
1✔
3016
        return SingletonResetContext(self)
1✔
3017

3018
    cpdef object _provide(self, tuple args, dict kwargs):
1✔
3019
        """Return single instance."""
3020
        if self._storage is None:
1✔
3021
            instance = __factory_call(self._instantiator, args, kwargs)
1✔
3022

3023
            if __is_future_or_coroutine(instance):
1✔
3024
                future_result = asyncio.Future()
1✔
3025
                instance = asyncio.ensure_future(instance)
1✔
3026
                instance.add_done_callback(functools.partial(self._async_init_instance, future_result))
1✔
3027
                self._storage = future_result
1✔
3028
                return future_result
1✔
3029

3030
            self._storage = instance
1✔
3031

3032
        return self._storage
1✔
3033

3034

3035
cdef class DelegatedSingleton(Singleton):
3036
    """Delegated singleton is injected "as is".
3037

3038
    .. py:attribute:: provided_type
3039

3040
        If provided type is defined, provider checks that providing class is
3041
        its subclass.
3042

3043
        :type: type | None
3044

3045
    .. py:attribute:: cls
3046
       :noindex:
3047

3048
        Class that provides object.
3049
        Alias for :py:attr:`provides`.
3050

3051
        :type: type
3052
    """
3053

3054
    __IS_DELEGATED__ = True
1✔
3055

3056

3057
cdef class ThreadSafeSingleton(BaseSingleton):
3058
    """Thread-safe singleton provider."""
3059

3060
    storage_lock = threading.RLock()
1✔
3061
    """Storage reentrant lock.
3062

3063
    :type: :py:class:`threading.RLock`
3064
    """
3065

3066
    def __init__(self, provides=None, *args, **kwargs):
3067
        """Initializer.
3068

3069
        :param provides: Provided type.
3070
        :type provides: type
3071
        """
3072
        self._storage = None
1✔
3073
        self._storage_lock = self.__class__.storage_lock
1✔
3074
        super(ThreadSafeSingleton, self).__init__(provides, *args, **kwargs)
1✔
3075

3076
    def reset(self):
1✔
3077
        """Reset cached instance, if any.
3078

3079
        :rtype: None
3080
        """
3081
        with self._storage_lock:
1✔
3082
            if __is_future_or_coroutine(self._storage):
1✔
3083
                asyncio.ensure_future(self._storage).cancel()
×
3084
            self._storage = None
1✔
3085
        return SingletonResetContext(self)
1✔
3086

3087
    cpdef object _provide(self, tuple args, dict kwargs):
1✔
3088
        """Return single instance."""
3089
        instance = self._storage
1✔
3090

3091
        if instance is None:
1✔
3092
            with self._storage_lock:
1✔
3093
                if self._storage is None:
1✔
3094
                    result = __factory_call(self._instantiator, args, kwargs)
1✔
3095
                    if __is_future_or_coroutine(result):
1✔
3096
                        future_result = asyncio.Future()
1✔
3097
                        result = asyncio.ensure_future(result)
1✔
3098
                        result.add_done_callback(functools.partial(self._async_init_instance, future_result))
1✔
3099
                        result = future_result
1✔
3100
                    self._storage = result
1✔
3101
                instance = self._storage
1✔
3102
        return instance
1✔
3103

3104

3105
cdef class DelegatedThreadSafeSingleton(ThreadSafeSingleton):
3106
    """Delegated thread-safe singleton is injected "as is".
3107

3108
    .. py:attribute:: provided_type
3109

3110
        If provided type is defined, provider checks that providing class is
3111
        its subclass.
3112

3113
        :type: type | None
3114

3115
    .. py:attribute:: cls
3116
       :noindex:
3117

3118
        Class that provides object.
3119
        Alias for :py:attr:`provides`.
3120

3121
        :type: type
3122
    """
3123

3124
    __IS_DELEGATED__ = True
1✔
3125

3126

3127
cdef class ThreadLocalSingleton(BaseSingleton):
3128
    """Thread-local singleton provides single objects in scope of thread.
3129

3130
    .. py:attribute:: provided_type
3131

3132
        If provided type is defined, provider checks that providing class is
3133
        its subclass.
3134

3135
        :type: type | None
3136

3137
    .. py:attribute:: cls
3138
       :noindex:
3139

3140
        Class that provides object.
3141
        Alias for :py:attr:`provides`.
3142

3143
        :type: type
3144
    """
3145

3146
    def __init__(self, provides=None, *args, **kwargs):
3147
        """Initializer.
3148

3149
        :param provides: Provided type.
3150
        :type provides: type
3151
        """
3152
        self._storage = threading.local()
1✔
3153
        super(ThreadLocalSingleton, self).__init__(provides, *args, **kwargs)
1✔
3154

3155
    def reset(self):
1✔
3156
        """Reset cached instance, if any.
3157

3158
        :rtype: None
3159
        """
3160
        try:
1✔
3161
            instance = self._storage.instance
1✔
3162
        except AttributeError:
1✔
3163
            return SingletonResetContext(self)
1✔
3164

3165
        if __is_future_or_coroutine(instance):
1✔
3166
            asyncio.ensure_future(instance).cancel()
×
3167

3168
        del self._storage.instance
1✔
3169

3170
        return SingletonResetContext(self)
1✔
3171

3172
    cpdef object _provide(self, tuple args, dict kwargs):
1✔
3173
        """Return single instance."""
3174
        cdef object instance
3175

3176
        try:
1✔
3177
            instance = self._storage.instance
1✔
3178
        except AttributeError:
1✔
3179
            instance = __factory_call(self._instantiator, args, kwargs)
1✔
3180

3181
            if __is_future_or_coroutine(instance):
1✔
3182
                future_result = asyncio.Future()
1✔
3183
                instance = asyncio.ensure_future(instance)
1✔
3184
                instance.add_done_callback(functools.partial(self._async_init_instance, future_result))
1✔
3185
                self._storage.instance = future_result
1✔
3186
                return future_result
1✔
3187

3188
            self._storage.instance = instance
1✔
3189
        
3190
        return instance
1✔
3191

3192
    def _async_init_instance(self, future_result, result):
1✔
3193
        try:
1✔
3194
            instance = result.result()
1✔
3195
        except Exception as exception:
1✔
3196
            del self._storage.instance
1✔
3197
            future_result.set_exception(exception)
1✔
3198
        else:
3199
            self._storage.instance = instance
1✔
3200
            future_result.set_result(instance)
1✔
3201

3202

3203
cdef class ContextLocalSingleton(BaseSingleton):
3204
    """Context-local singleton provides single objects in scope of a context.
3205

3206
    .. py:attribute:: provided_type
3207

3208
        If provided type is defined, provider checks that providing class is
3209
        its subclass.
3210

3211
        :type: type | None
3212

3213
    .. py:attribute:: cls
3214
       :noindex:
3215

3216
        Class that provides object.
3217
        Alias for :py:attr:`provides`.
3218

3219
        :type: type
3220
    """
3221
    _none = object()
1✔
3222

3223
    def __init__(self, provides=None, *args, **kwargs):
3224
        """Initializer.
3225

3226
        :param provides: Provided type.
3227
        :type provides: type
3228
        """
3229

3230

3231
        super(ContextLocalSingleton, self).__init__(provides, *args, **kwargs)
1✔
3232
        self._storage = ContextVar("_storage", default=self._none)
1✔
3233

3234
    def reset(self):
1✔
3235
        """Reset cached instance, if any.
3236

3237
        :rtype: None
3238
        """
3239
        instance = self._storage.get()
1✔
3240
        if instance is self._none:
1✔
3241
            return SingletonResetContext(self)
1✔
3242

3243
        if __is_future_or_coroutine(instance):
1✔
3244
            asyncio.ensure_future(instance).cancel()
×
3245

3246
        self._storage.set(self._none)
1✔
3247

3248
        return SingletonResetContext(self)
1✔
3249

3250
    cpdef object _provide(self, tuple args, dict kwargs):
1✔
3251
        """Return single instance."""
3252
        cdef object instance
3253

3254
        instance = self._storage.get()
1✔
3255

3256
        if instance is self._none:
1✔
3257
            instance = __factory_call(self._instantiator, args, kwargs)
1✔
3258

3259
            if __is_future_or_coroutine(instance):
1✔
3260
                future_result = asyncio.Future()
×
3261
                instance = asyncio.ensure_future(instance)
×
3262
                instance.add_done_callback(functools.partial(self._async_init_instance, future_result))
×
3263
                self._storage.set(future_result)
×
3264
                return future_result
×
3265

3266
            self._storage.set(instance)
1✔
3267

3268
        return instance
1✔
3269

3270
    def _async_init_instance(self, future_result, result):
1✔
3271
        try:
×
3272
            instance = result.result()
×
3273
        except Exception as exception:
×
3274
            self._storage.set(self._none)
×
3275
            future_result.set_exception(exception)
×
3276
        else:
3277
            self._storage.set(instance)
×
3278
            future_result.set_result(instance)
×
3279

3280

3281
cdef class DelegatedThreadLocalSingleton(ThreadLocalSingleton):
3282
    """Delegated thread-local singleton is injected "as is".
3283

3284
    .. py:attribute:: provided_type
3285

3286
        If provided type is defined, provider checks that providing class is
3287
        its subclass.
3288

3289
        :type: type | None
3290

3291
    .. py:attribute:: cls
3292
       :noindex:
3293

3294
        Class that provides object.
3295
        Alias for :py:attr:`provides`.
3296

3297
        :type: type
3298
    """
3299

3300
    __IS_DELEGATED__ = True
1✔
3301

3302

3303
cdef class AbstractSingleton(BaseSingleton):
3304
    """Abstract singleton provider.
3305

3306
    :py:class:`AbstractSingleton` is a :py:class:`Singleton` provider that must
3307
    be explicitly overridden before calling.
3308

3309
    Overriding of :py:class:`AbstractSingleton` is possible only by another
3310
    :py:class:`BaseSingleton` provider.
3311
    """
3312

3313
    def __call__(self, *args, **kwargs):
3314
        """Return provided object.
3315

3316
        Callable interface implementation.
3317
        """
3318
        if self._last_overriding is None:
1✔
3319
            raise Error("{0} must be overridden before calling".format(self))
1✔
3320
        return super().__call__(*args, **kwargs)
1✔
3321

3322
    def override(self, provider):
1✔
3323
        """Override provider with another provider.
3324

3325
        :param provider: Overriding provider.
3326
        :type provider: :py:class:`Provider`
3327

3328
        :raise: :py:exc:`dependency_injector.errors.Error`
3329

3330
        :return: Overriding context.
3331
        :rtype: :py:class:`OverridingContext`
3332
        """
3333
        if not isinstance(provider, BaseSingleton):
1✔
3334
            raise Error("{0} must be overridden only by "
1✔
3335
                        "{1} providers".format(self, BaseSingleton))
1✔
3336
        return super(AbstractSingleton, self).override(provider)
1✔
3337

3338
    def reset(self):
1✔
3339
        """Reset cached instance, if any.
3340

3341
        :rtype: None
3342
        """
3343
        if self._last_overriding is None:
1✔
3344
            raise Error("{0} must be overridden before calling".format(self))
1✔
3345
        return self._last_overriding.reset()
1✔
3346

3347

3348
cdef class SingletonDelegate(Delegate):
3349
    """Singleton delegate injects delegating singleton "as is".
3350

3351
    .. py:attribute:: provides
3352

3353
        Value that have to be provided.
3354

3355
        :type: object
3356
    """
3357

3358
    def __init__(self, singleton):
3359
        """Initializer.
3360

3361
        :param singleton: Value that have to be provided.
3362
        :type singleton: py:class:`BaseSingleton`
3363
        """
3364
        if isinstance(singleton, BaseSingleton) is False:
1✔
3365
            raise Error("{0} can wrap only {1} providers".format(
1✔
3366
                self.__class__, BaseSingleton))
1✔
3367
        super(SingletonDelegate, self).__init__(singleton)
1✔
3368

3369

3370
cdef class List(Provider):
3371
    """List provider provides a list of values.
3372

3373
    :py:class:`List` provider is needed for injecting a list of dependencies. It handles
3374
    positional argument injections the same way as :py:class:`Factory` provider.
3375

3376
    Keyword argument injections are not supported.
3377

3378
    .. code-block:: python
3379

3380
        dispatcher_factory = Factory(
3381
            Dispatcher,
3382
            modules=List(
3383
                Factory(ModuleA, dependency_a),
3384
                Factory(ModuleB, dependency_b),
3385
            ),
3386
        )
3387

3388
        dispatcher = dispatcher_factory()
3389

3390
        # is equivalent to:
3391

3392
        dispatcher = Dispatcher(
3393
            modules=[
3394
                ModuleA(dependency_a),
3395
                ModuleB(dependency_b),
3396
            ],
3397
        )
3398
    """
3399

3400
    def __init__(self, *args):
3401
        """Initializer."""
3402
        self._args = tuple()
1✔
3403
        self._args_len = 0
1✔
3404
        self.set_args(*args)
1✔
3405
        super(List, self).__init__()
1✔
3406

3407
    def __deepcopy__(self, memo):
1✔
3408
        """Create and return full copy of provider."""
3409
        copied = memo.get(id(self))
1✔
3410
        if copied is not None:
1✔
3411
            return copied
×
3412

3413
        copied = _memorized_duplicate(self, memo)
1✔
3414
        copied.set_args(*deepcopy_args(self, self.args, memo))
1✔
3415
        self._copy_overridings(copied, memo)
1✔
3416
        return copied
1✔
3417

3418
    def __str__(self):
3419
        """Return string representation of provider.
3420

3421
        :rtype: str
3422
        """
3423
        return represent_provider(provider=self, provides=list(self.args))
1✔
3424

3425
    @property
3426
    def args(self):
3427
        """Return positional argument injections."""
3428
        cdef int index
3429
        cdef PositionalInjection arg
3430
        cdef list args
3431

3432
        args = list()
1✔
3433
        for index in range(self._args_len):
1✔
3434
            arg = self._args[index]
1✔
3435
            args.append(arg._value)
1✔
3436
        return tuple(args)
1✔
3437

3438
    def add_args(self, *args):
1✔
3439
        """Add positional argument injections.
3440

3441
        :return: Reference ``self``
3442
        """
3443
        self._args += parse_positional_injections(args)
1✔
3444
        self._args_len = len(self._args)
1✔
3445
        return self
1✔
3446

3447
    def set_args(self, *args):
1✔
3448
        """Set positional argument injections.
3449

3450
        Existing positional argument injections are dropped.
3451

3452
        :return: Reference ``self``
3453
        """
3454
        self._args = parse_positional_injections(args)
1✔
3455
        self._args_len = len(self._args)
1✔
3456
        return self
1✔
3457

3458
    def clear_args(self):
1✔
3459
        """Drop positional argument injections.
3460

3461
        :return: Reference ``self``
3462
        """
3463
        self._args = tuple()
1✔
3464
        self._args_len = len(self._args)
1✔
3465
        return self
1✔
3466

3467
    @property
3468
    def related(self):
3469
        """Return related providers generator."""
3470
        yield from filter(is_provider, self.args)
1✔
3471
        yield from super().related
1✔
3472

3473
    cpdef object _provide(self, tuple args, dict kwargs):
1✔
3474
        """Return result of provided callable call."""
3475
        return __provide_positional_args(args, self._args, self._args_len, self._async_mode)
1✔
3476

3477

3478
cdef class Dict(Provider):
3479
    """Dict provider provides a dictionary of values.
3480

3481
    :py:class:`Dict` provider is needed for injecting a dictionary of dependencies. It handles
3482
    keyword argument injections the same way as :py:class:`Factory` provider.
3483

3484
    Positional argument injections are not supported.
3485

3486
    .. code-block:: python
3487

3488
        dispatcher_factory = Factory(
3489
            Dispatcher,
3490
            modules=Dict(
3491
                module1=Factory(ModuleA, dependency_a),
3492
                module2=Factory(ModuleB, dependency_b),
3493
            ),
3494
        )
3495

3496
        dispatcher = dispatcher_factory()
3497

3498
        # is equivalent to:
3499

3500
        dispatcher = Dispatcher(
3501
            modules={
3502
                "module1": ModuleA(dependency_a),
3503
                "module2": ModuleB(dependency_b),
3504
            },
3505
        )
3506
    """
3507

3508
    def __init__(self, dict_=None, **kwargs):
3509
        """Initializer."""
3510
        self._kwargs = tuple()
1✔
3511
        self._kwargs_len = 0
1✔
3512
        self.add_kwargs(dict_, **kwargs)
1✔
3513
        super(Dict, self).__init__()
1✔
3514

3515
    def __deepcopy__(self, memo):
1✔
3516
        """Create and return full copy of provider."""
3517
        copied = memo.get(id(self))
1✔
3518
        if copied is not None:
1✔
3519
            return copied
×
3520

3521
        copied = _memorized_duplicate(self, memo)
1✔
3522
        self._copy_kwargs(copied, memo)
1✔
3523
        self._copy_overridings(copied, memo)
1✔
3524
        return copied
1✔
3525

3526
    def __str__(self):
3527
        """Return string representation of provider.
3528

3529
        :rtype: str
3530
        """
3531
        return represent_provider(provider=self, provides=self.kwargs)
1✔
3532

3533
    @property
3534
    def kwargs(self):
3535
        """Return keyword argument injections."""
3536
        cdef int index
3537
        cdef NamedInjection kwarg
3538
        cdef dict kwargs
3539

3540
        kwargs = dict()
1✔
3541
        for index in range(self._kwargs_len):
1✔
3542
            kwarg = self._kwargs[index]
1✔
3543
            kwargs[kwarg._name] = kwarg._value
1✔
3544
        return kwargs
1✔
3545

3546
    def add_kwargs(self, dict_=None, **kwargs):
1✔
3547
        """Add keyword argument injections.
3548

3549
        :return: Reference ``self``
3550
        """
3551
        if dict_ is None:
1✔
3552
            dict_ = {}
1✔
3553

3554
        self._kwargs += parse_named_injections(dict_)
1✔
3555
        self._kwargs += parse_named_injections(kwargs)
1✔
3556
        self._kwargs_len = len(self._kwargs)
1✔
3557

3558
        return self
1✔
3559

3560
    def set_kwargs(self, dict_=None, **kwargs):
1✔
3561
        """Set keyword argument injections.
3562

3563
        Existing keyword argument injections are dropped.
3564

3565
        :return: Reference ``self``
3566
        """
3567
        if dict_ is None:
1✔
3568
            dict_ = {}
1✔
3569

3570
        self._kwargs = parse_named_injections(dict_)
1✔
3571
        self._kwargs += parse_named_injections(kwargs)
1✔
3572
        self._kwargs_len = len(self._kwargs)
1✔
3573

3574
        return self
1✔
3575

3576
    def clear_kwargs(self):
1✔
3577
        """Drop keyword argument injections.
3578

3579
        :return: Reference ``self``
3580
        """
3581
        self._kwargs = tuple()
1✔
3582
        self._kwargs_len = len(self._kwargs)
1✔
3583
        return self
1✔
3584

3585
    @property
3586
    def related(self):
3587
        """Return related providers generator."""
3588
        yield from filter(is_provider, self.kwargs.values())
1✔
3589
        yield from super().related
1✔
3590

3591
    def _copy_kwargs(self, copied, memo):
1✔
3592
        """Return copy of kwargs."""
3593
        copied_kwargs = {
1✔
3594
            _copy_if_provider(name, memo): _copy_if_provider(value, memo)
1✔
3595
            for name, value in self.kwargs.items()
1✔
3596
        }
3597
        copied.set_kwargs(copied_kwargs)
1✔
3598

3599
    cpdef object _provide(self, tuple args, dict kwargs):
1✔
3600
        """Return result of provided callable call."""
3601
        return __provide_keyword_args(kwargs, self._kwargs, self._kwargs_len, self._async_mode)
1✔
3602

3603

3604
@cython.no_gc
3605
cdef class NullAwaitable:
3606
    def __next__(self):
3607
        raise StopIteration from None
1✔
3608

3609
    def __await__(self):
3610
        return self
1✔
3611

3612

3613
cdef NullAwaitable NULL_AWAITABLE = NullAwaitable()
1✔
3614

3615

3616
cdef class Resource(Provider):
3617
    """Resource provider provides a component with initialization and shutdown."""
3618

3619
    def __init__(self, provides=None, *args, **kwargs):
3620
        self._provides = None
1✔
3621
        self.set_provides(provides)
1✔
3622

3623
        self._initialized = False
1✔
3624
        self._resource = None
1✔
3625
        self._shutdowner = None
1✔
3626

3627
        self._args = tuple()
1✔
3628
        self._args_len = 0
1✔
3629
        self.set_args(*args)
1✔
3630

3631
        self._kwargs = tuple()
1✔
3632
        self._kwargs_len = 0
1✔
3633
        self.set_kwargs(**kwargs)
1✔
3634

3635
        super().__init__()
1✔
3636

3637
    def __deepcopy__(self, memo):
1✔
3638
        """Create and return full copy of provider."""
3639
        copied = memo.get(id(self))
1✔
3640
        if copied is not None:
1✔
3641
            return copied
×
3642

3643
        if self._initialized:
1✔
3644
            raise Error("Can not copy initialized resource")
1✔
3645

3646
        copied = _memorized_duplicate(self, memo)
1✔
3647
        copied.set_provides(_copy_if_provider(self.provides, memo))
1✔
3648
        copied.set_args(*deepcopy_args(self, self.args, memo))
1✔
3649
        copied.set_kwargs(**deepcopy_kwargs(self, self.kwargs, memo))
1✔
3650

3651
        self._copy_overridings(copied, memo)
1✔
3652

3653
        return copied
1✔
3654

3655
    def __str__(self):
3656
        """Return string representation of provider.
3657

3658
        :rtype: str
3659
        """
3660
        return represent_provider(provider=self, provides=self.provides)
1✔
3661

3662
    @property
3663
    def provides(self):
3664
        """Return provider provides."""
3665
        return self._provides
1✔
3666

3667
    def set_provides(self, provides):
1✔
3668
        """Set provider provides."""
3669
        provides = _resolve_string_import(provides)
1✔
3670

3671
        if isasyncgenfunction(provides):
1✔
3672
            provides = asynccontextmanager(provides)
1✔
3673
        elif isgeneratorfunction(provides):
1✔
3674
            provides = contextmanager(provides)
1✔
3675

3676
        self._provides = provides
1✔
3677
        return self
1✔
3678

3679
    @property
3680
    def args(self):
3681
        """Return positional argument injections."""
3682
        cdef int index
3683
        cdef PositionalInjection arg
3684
        cdef list args
3685

3686
        args = list()
1✔
3687
        for index in range(self._args_len):
1✔
3688
            arg = self._args[index]
1✔
3689
            args.append(arg._value)
1✔
3690
        return tuple(args)
1✔
3691

3692
    def add_args(self, *args):
1✔
3693
        """Add positional argument injections.
3694

3695
        :return: Reference ``self``
3696
        """
3697
        self._args += parse_positional_injections(args)
1✔
3698
        self._args_len = len(self._args)
1✔
3699
        return self
1✔
3700

3701
    def set_args(self, *args):
1✔
3702
        """Set positional argument injections.
3703

3704
        Existing positional argument injections are dropped.
3705

3706
        :return: Reference ``self``
3707
        """
3708
        self._args = parse_positional_injections(args)
1✔
3709
        self._args_len = len(self._args)
1✔
3710
        return self
1✔
3711

3712
    def clear_args(self):
1✔
3713
        """Drop positional argument injections.
3714

3715
        :return: Reference ``self``
3716
        """
3717
        self._args = tuple()
1✔
3718
        self._args_len = len(self._args)
1✔
3719
        return self
1✔
3720

3721
    @property
3722
    def kwargs(self):
3723
        """Return keyword argument injections."""
3724
        cdef int index
3725
        cdef NamedInjection kwarg
3726
        cdef dict kwargs
3727

3728
        kwargs = dict()
1✔
3729
        for index in range(self._kwargs_len):
1✔
3730
            kwarg = self._kwargs[index]
1✔
3731
            kwargs[kwarg._name] = kwarg._value
1✔
3732
        return kwargs
1✔
3733

3734
    def add_kwargs(self, **kwargs):
1✔
3735
        """Add keyword argument injections.
3736

3737
        :return: Reference ``self``
3738
        """
3739
        self._kwargs += parse_named_injections(kwargs)
1✔
3740
        self._kwargs_len = len(self._kwargs)
1✔
3741
        return self
1✔
3742

3743
    def set_kwargs(self, **kwargs):
1✔
3744
        """Set keyword argument injections.
3745

3746
        Existing keyword argument injections are dropped.
3747

3748
        :return: Reference ``self``
3749
        """
3750
        self._kwargs = parse_named_injections(kwargs)
1✔
3751
        self._kwargs_len = len(self._kwargs)
1✔
3752
        return self
1✔
3753

3754
    def clear_kwargs(self):
1✔
3755
        """Drop keyword argument injections.
3756

3757
        :return: Reference ``self``
3758
        """
3759
        self._kwargs = tuple()
1✔
3760
        self._kwargs_len = len(self._kwargs)
1✔
3761
        return self
1✔
3762

3763
    @property
3764
    def initialized(self):
3765
        """Check if resource is initialized."""
3766
        return self._initialized
1✔
3767

3768
    def init(self):
1✔
3769
        """Initialize resource."""
3770
        return self.__call__()
1✔
3771

3772
    def shutdown(self):
1✔
3773
        """Shutdown resource."""
3774
        if not self._initialized:
1✔
3775
            if self._async_mode == ASYNC_MODE_ENABLED:
1✔
3776
                return NULL_AWAITABLE
1✔
3777
            return
1✔
3778

3779
        if self._shutdowner:
1✔
3780
            future = self._shutdowner(None, None, None)
1✔
3781

3782
            if __is_future_or_coroutine(future):
1✔
3783
                return ensure_future(self._shutdown_async(future))
1✔
3784

3785
        self._resource = None
1✔
3786
        self._initialized = False
1✔
3787
        self._shutdowner = None
1✔
3788

3789
        if self._async_mode == ASYNC_MODE_ENABLED:
1✔
3790
            return NULL_AWAITABLE
1✔
3791

3792
    @property
3793
    def related(self):
3794
        """Return related providers generator."""
3795
        yield from filter(is_provider, [self.provides])
1✔
3796
        yield from filter(is_provider, self.args)
1✔
3797
        yield from filter(is_provider, self.kwargs.values())
1✔
3798
        yield from super().related
1✔
3799

3800
    async def _shutdown_async(self, future) -> None:
1✔
3801
        try:
1✔
3802
            await future
1✔
3803
        finally:
3804
            self._resource = None
1✔
3805
            self._initialized = False
1✔
3806
            self._shutdowner = None
1✔
3807

3808
    async def _handle_async_cm(self, obj) -> None:
1✔
3809
        try:
1✔
3810
            self._resource = resource = await obj.__aenter__()
1✔
3811
            self._shutdowner = obj.__aexit__
1✔
3812
            return resource
1✔
3813
        except:
1✔
3814
            self._initialized = False
1✔
3815
            raise
1✔
3816

3817
    async def _provide_async(self, future) -> None:
1✔
3818
        try:
1✔
3819
            obj = await future
1✔
3820

3821
            if hasattr(obj, '__aenter__') and hasattr(obj, '__aexit__'):
1✔
3822
                self._resource = await obj.__aenter__()
1✔
3823
                self._shutdowner = obj.__aexit__
1✔
3824
            elif hasattr(obj, '__enter__') and hasattr(obj, '__exit__'):
1✔
3825
                self._resource = obj.__enter__()
×
3826
                self._shutdowner = obj.__exit__
×
3827
            else:
3828
                self._resource = obj
1✔
3829
                self._shutdowner = None
1✔
3830

3831
            return self._resource
1✔
3832
        except:
1✔
3833
            self._initialized = False
1✔
3834
            raise
1✔
3835

3836
    cpdef object _provide(self, tuple args, dict kwargs):
1✔
3837
        if self._initialized:
1✔
3838
            return self._resource
1✔
3839

3840
        obj = __call(
1✔
3841
            self._provides,
1✔
3842
            args,
3843
            self._args,
1✔
3844
            self._args_len,
3845
            kwargs,
3846
            self._kwargs,
1✔
3847
            self._kwargs_len,
3848
            self._async_mode,
3849
        )
3850

3851
        if __is_future_or_coroutine(obj):
1✔
3852
            self._initialized = True
1✔
3853
            self._resource = resource = ensure_future(self._provide_async(obj))
1✔
3854
            return resource
1✔
3855
        elif hasattr(obj, '__enter__') and hasattr(obj, '__exit__'):
1✔
3856
            self._resource = obj.__enter__()
1✔
3857
            self._shutdowner = obj.__exit__
1✔
3858
        elif hasattr(obj, '__aenter__') and hasattr(obj, '__aexit__'):
1✔
3859
            self._initialized = True
1✔
3860
            self._resource = resource = ensure_future(self._handle_async_cm(obj))
1✔
3861
            return resource
1✔
3862
        else:
3863
            self._resource = obj
1✔
3864
            self._shutdowner = None
1✔
3865

3866
        self._initialized = True
1✔
3867
        return self._resource
1✔
3868

3869

3870
cdef class Container(Provider):
3871
    """Container provider provides an instance of declarative container.
3872

3873
    .. warning::
3874
        Provider is experimental. Its interface may change.
3875
    """
3876

3877
    def __init__(self, container_cls=None, container=None, **overriding_providers):
3878
        """Initialize provider."""
3879
        self._container_cls = container_cls
1✔
3880
        self._overriding_providers = overriding_providers
1✔
3881

3882
        if container is None and container_cls:
1✔
3883
            container = container_cls()
1✔
3884
            container.assign_parent(self)
1✔
3885
        self._container = container
1✔
3886

3887
        if self._container and self._overriding_providers:
1✔
3888
            self.apply_overridings()
×
3889

3890
        self._parent = None
1✔
3891

3892
        super(Container, self).__init__()
1✔
3893

3894
    def __deepcopy__(self, memo):
1✔
3895
        """Create and return full copy of provider."""
3896
        cdef Container copied
3897

3898
        copied = memo.get(id(self))
1✔
3899
        if copied is not None:
1✔
3900
            return copied
×
3901

3902
        copied = <Container> _memorized_duplicate(self, memo)
1✔
3903
        copied._container_cls = self._container_cls
1✔
3904
        copied._container = deepcopy(self._container, memo)
1✔
3905
        copied._overriding_providers = deepcopy(self._overriding_providers, memo)
1✔
3906
        self._copy_parent(copied, memo)
1✔
3907
        self._copy_overridings(copied, memo)
1✔
3908
        return copied
1✔
3909

3910
    def __getattr__(self, name):
3911
        """Return dependency provider."""
3912
        if name.startswith("__") and name.endswith("__"):
1✔
3913
            raise AttributeError(
1✔
3914
                "'{cls}' object has no attribute "
3915
                "'{attribute_name}'".format(cls=self.__class__.__name__, attribute_name=name))
1✔
3916
        return getattr(self._container, name)
1✔
3917

3918
    @property
3919
    def providers(self):
3920
        return self._container.providers
1✔
3921

3922
    @property
3923
    def container(self):
3924
        return self._container
1✔
3925

3926
    def override(self, provider):
1✔
3927
        """Override provider with another provider."""
3928
        if not hasattr(provider, "providers"):
1✔
3929
            raise Error("Container provider {0} can be overridden only by providers container".format(self))
1✔
3930

3931
        self._container.override_providers(**provider.providers)
1✔
3932
        return super().override(provider)
1✔
3933

3934
    def reset_last_overriding(self):
1✔
3935
        """Reset last overriding provider.
3936

3937
        :raise: :py:exc:`dependency_injector.errors.Error` if provider is not
3938
                overridden.
3939

3940
        :rtype: None
3941
        """
3942
        super().reset_last_overriding()
1✔
3943
        for provider in self._container.providers.values():
1✔
3944
            if not provider.overridden:
1✔
3945
                continue
1✔
3946
            provider.reset_last_overriding()
1✔
3947

3948
    def reset_override(self):
1✔
3949
        """Reset all overriding providers.
3950

3951
        :rtype: None
3952
        """
3953
        super().reset_override()
1✔
3954
        for provider in self._container.providers.values():
1✔
3955
            if not provider.overridden:
1✔
3956
                continue
1✔
3957
            provider.reset_override()
1✔
3958

3959
    def apply_overridings(self):
1✔
3960
        """Apply container overriding.
3961

3962
        This method should not be called directly. It is called on
3963
        declarative container initialization."""
3964
        self._container.override_providers(**self._overriding_providers)
1✔
3965

3966
    @property
3967
    def related(self):
3968
        """Return related providers generator."""
3969
        yield from self.providers.values()
1✔
3970
        yield from super().related
1✔
3971

3972
    def resolve_provider_name(self, provider):
1✔
3973
        """Try to resolve provider name."""
3974
        for provider_name, container_provider in self.providers.items():
1✔
3975
            if container_provider is provider:
1✔
3976
                return provider_name
1✔
3977
        else:
3978
            raise Error(f"Can not resolve name for provider \"{provider}\"")
1✔
3979

3980
    @property
3981
    def parent(self):
3982
        """Return parent."""
3983
        return self._parent
1✔
3984

3985
    @property
3986
    def parent_name(self):
3987
        """Return parent name."""
3988
        if not self._parent:
1✔
3989
            return None
1✔
3990

3991
        name = ""
1✔
3992
        if self._parent.parent_name:
1✔
3993
            name += f"{self._parent.parent_name}."
1✔
3994
        name += f"{self._parent.resolve_provider_name(self)}"
1✔
3995

3996
        return name
1✔
3997

3998
    def assign_parent(self, parent):
1✔
3999
        """Assign parent."""
4000
        self._parent = parent
1✔
4001

4002
    def _copy_parent(self, copied, memo):
1✔
4003
        _copy_parent(self, copied, memo)
1✔
4004

4005
    cpdef object _provide(self, tuple args, dict kwargs):
1✔
4006
        """Return single instance."""
4007
        return self._container
1✔
4008

4009

4010
cdef class Selector(Provider):
4011
    """Selector provider selects provider based on the configuration value or other callable.
4012

4013
    :py:class:`Selector` provider has a callable called ``selector`` and a dictionary of providers.
4014

4015
    The ``selector`` callable is provided as a first positional argument. It can be
4016
    :py:class:`Configuration` provider or any other callable. It has to return a string value.
4017
    That value is used as a key for selecting the provider from the dictionary of providers.
4018

4019
    The providers are provided as keyword arguments. Argument name is used as a key for
4020
    selecting the provider.
4021

4022
    .. code-block:: python
4023

4024
        config = Configuration()
4025

4026
        selector = Selector(
4027
            config.one_or_another,
4028
            one=providers.Factory(SomeClass),
4029
            another=providers.Factory(SomeOtherClass),
4030
        )
4031

4032
        config.override({"one_or_another": "one"})
4033
        instance_1 = selector()
4034
        assert isinstance(instance_1, SomeClass)
4035

4036
        config.override({"one_or_another": "another"})
4037
        instance_2 = selector()
4038
        assert isinstance(instance_2, SomeOtherClass)
4039
    """
4040

4041
    def __init__(self, selector=None, **providers):
4042
        """Initialize provider."""
4043
        self._selector = None
1✔
4044
        self.set_selector(selector)
1✔
4045

4046
        self._providers = {}
1✔
4047
        self.set_providers(**providers)
1✔
4048

4049
        super(Selector, self).__init__()
1✔
4050

4051
    def __deepcopy__(self, memo):
1✔
4052
        """Create and return full copy of provider."""
4053
        copied = memo.get(id(self))
1✔
4054
        if copied is not None:
1✔
4055
            return copied
×
4056

4057
        copied = _memorized_duplicate(self, memo)
1✔
4058
        copied.set_selector(deepcopy(self._selector, memo))
1✔
4059
        copied.set_providers(**deepcopy(self._providers, memo))
1✔
4060

4061
        self._copy_overridings(copied, memo)
1✔
4062

4063
        return copied
1✔
4064

4065
    def __getattr__(self, name):
4066
        """Return provider."""
4067
        if name.startswith("__") and name.endswith("__"):
1✔
4068
            raise AttributeError(
1✔
4069
                "'{cls}' object has no attribute "
4070
                "'{attribute_name}'".format(cls=self.__class__.__name__, attribute_name=name))
1✔
4071
        if name not in self._providers:
1✔
4072
            raise AttributeError("Selector has no \"{0}\" provider".format(name))
1✔
4073

4074
        return self._providers[name]
1✔
4075

4076
    def __str__(self):
4077
        """Return string representation of provider.
4078

4079
        :rtype: str
4080
        """
4081

4082
        return "<{provider}({selector}, {providers}) at {address}>".format(
1✔
4083
            provider=".".join(( self.__class__.__module__, self.__class__.__name__)),
1✔
4084
            selector=self._selector,
4085
            providers=", ".join((
1✔
4086
                "{0}={1}".format(name, provider)
1✔
4087
                for name, provider in self._providers.items()
1✔
4088
            )),
4089
            address=hex(id(self)),
1✔
4090
        )
4091

4092
    @property
4093
    def selector(self):
4094
        """Return selector."""
4095
        return self._selector
1✔
4096

4097
    def set_selector(self, selector):
1✔
4098
        """Set selector."""
4099
        self._selector = selector
1✔
4100
        return self
1✔
4101

4102
    @property
4103
    def providers(self):
4104
        """Return providers."""
4105
        return dict(self._providers)
1✔
4106

4107
    def set_providers(self, **providers: Provider):
1✔
4108
        """Set providers."""
4109
        self._providers = providers
1✔
4110
        return self
1✔
4111

4112
    @property
4113
    def related(self):
4114
        """Return related providers generator."""
4115
        yield from filter(is_provider, [self._selector])
1✔
4116
        yield from self.providers.values()
1✔
4117
        yield from super().related
1✔
4118

4119
    cpdef object _provide(self, tuple args, dict kwargs):
1✔
4120
        """Return single instance."""
4121
        selector_value = self._selector()
1✔
4122

4123
        if selector_value is None:
1✔
4124
            raise Error("Selector value is undefined")
1✔
4125

4126
        if selector_value not in self._providers:
1✔
4127
            raise Error("Selector has no \"{0}\" provider".format(selector_value))
1✔
4128

4129
        return self._providers[selector_value](*args, **kwargs)
1✔
4130

4131

4132
cdef class ProvidedInstance(Provider):
4133
    """Provider that helps to inject attributes and items of the injected instance.
4134

4135
    You can use it like that:
4136

4137
    .. code-block:: python
4138

4139
       service = providers.Singleton(Service)
4140

4141
       client_factory = providers.Factory(
4142
           Client,
4143
           value1=service.provided[0],
4144
           value2=service.provided.value,
4145
           value3=service.provided.values[0],
4146
           value4=service.provided.get_value.call(),
4147
       )
4148

4149
    You should not create this provider directly. Get it from the ``.provided`` attribute of the
4150
    injected provider. This attribute returns the :py:class:`ProvidedInstance` for that provider.
4151

4152
    Providers that have ``.provided`` attribute:
4153

4154
    - :py:class:`Callable` and its subclasses
4155
    - :py:class:`Factory` and its subclasses
4156
    - :py:class:`Singleton` and its subclasses
4157
    - :py:class:`Object`
4158
    - :py:class:`List`
4159
    - :py:class:`Selector`
4160
    - :py:class:`Dependency`
4161
    """
4162

4163
    def __init__(self, provides=None):
4164
        self._provides = None
1✔
4165
        self.set_provides(provides)
1✔
4166
        super().__init__()
1✔
4167

4168
    def __repr__(self):
4169
        return f"{self.__class__.__name__}(\"{self._provides}\")"
1✔
4170

4171
    def __deepcopy__(self, memo):
1✔
4172
        copied = memo.get(id(self))
1✔
4173
        if copied is not None:
1✔
4174
            return copied
×
4175

4176
        copied = _memorized_duplicate(self, memo)
1✔
4177
        copied.set_provides(_copy_if_provider(self.provides, memo))
1✔
4178
        return copied
1✔
4179

4180
    def __getattr__(self, item):
4181
        return AttributeGetter(self, item)
1✔
4182

4183
    def __getitem__(self, item):
4184
        return ItemGetter(self, item)
1✔
4185

4186
    @property
4187
    def provides(self):
4188
        """Return provider provides."""
4189
        return self._provides
1✔
4190

4191
    def set_provides(self, provides):
1✔
4192
        """Set provider provides."""
4193
        self._provides = provides
1✔
4194
        return self
1✔
4195

4196
    def call(self, *args, **kwargs):
1✔
4197
        return MethodCaller(self, *args, **kwargs)
1✔
4198

4199
    @property
4200
    def related(self):
4201
        """Return related providers generator."""
4202
        if is_provider(self.provides):
1✔
4203
            yield self.provides
1✔
4204
        yield from super().related
1✔
4205

4206
    cpdef object _provide(self, tuple args, dict kwargs):
1✔
4207
        return self._provides(*args, **kwargs)
1✔
4208

4209

4210
cdef class AttributeGetter(Provider):
4211
    """Provider that returns the attribute of the injected instance.
4212

4213
    You should not create this provider directly. See :py:class:`ProvidedInstance` instead.
4214
    """
4215

4216
    def __init__(self, provides=None, name=None):
4217
        self._provides = None
1✔
4218
        self.set_provides(provides)
1✔
4219

4220
        self._name = None
1✔
4221
        self.set_name(name)
1✔
4222
        super().__init__()
1✔
4223

4224
    def __repr__(self):
4225
        return f"{self.__class__.__name__}(\"{self.name}\")"
1✔
4226

4227
    def __deepcopy__(self, memo):
1✔
4228
        copied = memo.get(id(self))
1✔
4229
        if copied is not None:
1✔
4230
            return copied
×
4231

4232
        copied = _memorized_duplicate(self, memo)
1✔
4233
        copied.set_provides(_copy_if_provider(self.provides, memo))
1✔
4234
        copied.set_name(self.name)
1✔
4235
        return copied
1✔
4236

4237
    def __getattr__(self, item):
4238
        return AttributeGetter(self, item)
1✔
4239

4240
    def __getitem__(self, item):
4241
        return ItemGetter(self, item)
1✔
4242

4243
    @property
4244
    def provides(self):
4245
        """Return provider provides."""
4246
        return self._provides
1✔
4247

4248
    def set_provides(self, provides):
1✔
4249
        """Set provider provides."""
4250
        self._provides = provides
1✔
4251
        return self
1✔
4252

4253
    @property
4254
    def name(self):
4255
        """Return name of the attribute."""
4256
        return self._name
1✔
4257

4258
    def set_name(self, name):
1✔
4259
        """Set name of the attribute."""
4260
        self._name = name
1✔
4261
        return self
1✔
4262

4263
    def call(self, *args, **kwargs):
1✔
4264
        return MethodCaller(self, *args, **kwargs)
1✔
4265

4266
    @property
4267
    def related(self):
4268
        """Return related providers generator."""
4269
        if is_provider(self.provides):
1✔
4270
            yield self.provides
1✔
4271
        yield from super().related
1✔
4272

4273
    cpdef object _provide(self, tuple args, dict kwargs):
1✔
4274
        provided = self.provides(*args, **kwargs)
1✔
4275
        if __is_future_or_coroutine(provided):
1✔
4276
            future_result = asyncio.Future()
1✔
4277
            provided = asyncio.ensure_future(provided)
1✔
4278
            provided.add_done_callback(functools.partial(self._async_provide, future_result))
1✔
4279
            return future_result
1✔
4280
        return getattr(provided, self.name)
1✔
4281

4282
    def _async_provide(self, future_result, future):
1✔
4283
        try:
1✔
4284
            provided = future.result()
1✔
4285
            result = getattr(provided, self.name)
1✔
4286
        except Exception as exception:
1✔
4287
            future_result.set_exception(exception)
1✔
4288
        else:
4289
            future_result.set_result(result)
1✔
4290

4291

4292
cdef class ItemGetter(Provider):
4293
    """Provider that returns the item of the injected instance.
4294

4295
    You should not create this provider directly. See :py:class:`ProvidedInstance` instead.
4296
    """
4297

4298
    def __init__(self, provides=None, name=None):
4299
        self._provides = None
1✔
4300
        self.set_provides(provides)
1✔
4301

4302
        self._name = None
1✔
4303
        self.set_name(name)
1✔
4304
        super().__init__()
1✔
4305

4306
    def __repr__(self):
4307
        return f"{self.__class__.__name__}(\"{self.name}\")"
1✔
4308

4309
    def __deepcopy__(self, memo):
1✔
4310
        copied = memo.get(id(self))
1✔
4311
        if copied is not None:
1✔
4312
            return copied
×
4313

4314
        copied = _memorized_duplicate(self, memo)
1✔
4315
        copied.set_provides(_copy_if_provider(self.provides, memo))
1✔
4316
        copied.set_name(self.name)
1✔
4317
        return copied
1✔
4318

4319
    def __getattr__(self, item):
4320
        return AttributeGetter(self, item)
1✔
4321

4322
    def __getitem__(self, item):
4323
        return ItemGetter(self, item)
1✔
4324

4325
    @property
4326
    def provides(self):
4327
        """Return provider"s provides."""
4328
        return self._provides
1✔
4329

4330
    def set_provides(self, provides):
1✔
4331
        """Set provider"s provides."""
4332
        self._provides = provides
1✔
4333
        return self
1✔
4334

4335
    @property
4336
    def name(self):
4337
        """Return name of the item."""
4338
        return self._name
1✔
4339

4340
    def set_name(self, name):
1✔
4341
        """Set name of the item."""
4342
        self._name = name
1✔
4343
        return self
1✔
4344

4345
    def call(self, *args, **kwargs):
1✔
4346
        return MethodCaller(self, *args, **kwargs)
1✔
4347

4348
    @property
4349
    def related(self):
4350
        """Return related providers generator."""
4351
        if is_provider(self.provides):
1✔
4352
            yield self.provides
1✔
4353
        yield from super().related
1✔
4354

4355
    cpdef object _provide(self, tuple args, dict kwargs):
1✔
4356
        provided = self.provides(*args, **kwargs)
1✔
4357
        if __is_future_or_coroutine(provided):
1✔
4358
            future_result = asyncio.Future()
1✔
4359
            provided = asyncio.ensure_future(provided)
1✔
4360
            provided.add_done_callback(functools.partial(self._async_provide, future_result))
1✔
4361
            return future_result
1✔
4362
        return provided[self.name]
1✔
4363

4364
    def _async_provide(self, future_result, future):
1✔
4365
        try:
1✔
4366
            provided = future.result()
1✔
4367
            result = provided[self.name]
1✔
4368
        except Exception as exception:
1✔
4369
            future_result.set_exception(exception)
1✔
4370
        else:
4371
            future_result.set_result(result)
1✔
4372

4373

4374
cdef class MethodCaller(Provider):
4375
    """Provider that calls the method of the injected instance.
4376

4377
    You should not create this provider directly. See :py:class:`ProvidedInstance` instead.
4378
    """
4379

4380
    def __init__(self, provides=None, *args, **kwargs):
4381
        self._provides = None
1✔
4382
        self.set_provides(provides)
1✔
4383

4384
        self._args = tuple()
1✔
4385
        self._args_len = 0
1✔
4386
        self.set_args(*args)
1✔
4387

4388
        self._kwargs = tuple()
1✔
4389
        self._kwargs_len = 0
1✔
4390
        self.set_kwargs(**kwargs)
1✔
4391

4392
        super().__init__()
1✔
4393

4394
    def __repr__(self):
4395
        return f"{self.__class__.__name__}({self.provides})"
×
4396

4397
    def __deepcopy__(self, memo):
1✔
4398
        copied = memo.get(id(self))
1✔
4399
        if copied is not None:
1✔
4400
            return copied
×
4401

4402
        copied = _memorized_duplicate(self, memo)
1✔
4403
        copied.set_provides(_copy_if_provider(self.provides, memo))
1✔
4404
        copied.set_args(*deepcopy_args(self, self.args, memo))
1✔
4405
        copied.set_kwargs(**deepcopy_kwargs(self, self.kwargs, memo))
1✔
4406
        self._copy_overridings(copied, memo)
1✔
4407
        return copied
1✔
4408

4409
    def __getattr__(self, item):
4410
        return AttributeGetter(self, item)
1✔
4411

4412
    def __getitem__(self, item):
4413
        return ItemGetter(self, item)
1✔
4414

4415
    def call(self, *args, **kwargs):
1✔
4416
        return MethodCaller(self, *args, **kwargs)
1✔
4417

4418
    @property
4419
    def provides(self):
4420
        """Return provider provides."""
4421
        return self._provides
1✔
4422

4423
    def set_provides(self, provides):
1✔
4424
        """Set provider provides."""
4425
        self._provides = provides
1✔
4426
        return self
1✔
4427

4428
    @property
4429
    def args(self):
4430
        """Return positional argument injections."""
4431
        cdef int index
4432
        cdef PositionalInjection arg
4433
        cdef list args
4434

4435
        args = list()
1✔
4436
        for index in range(self._args_len):
1✔
4437
            arg = self._args[index]
1✔
4438
            args.append(arg._value)
1✔
4439
        return tuple(args)
1✔
4440

4441
    def set_args(self, *args):
1✔
4442
        """Set positional argument injections.
4443

4444
        Existing positional argument injections are dropped.
4445

4446
        :return: Reference ``self``
4447
        """
4448
        self._args = parse_positional_injections(args)
1✔
4449
        self._args_len = len(self._args)
1✔
4450
        return self
1✔
4451

4452
    @property
4453
    def kwargs(self):
4454
        """Return keyword argument injections."""
4455
        cdef int index
4456
        cdef NamedInjection kwarg
4457
        cdef dict kwargs
4458

4459
        kwargs = dict()
1✔
4460
        for index in range(self._kwargs_len):
1✔
4461
            kwarg = self._kwargs[index]
1✔
4462
            kwargs[kwarg._name] = kwarg._value
1✔
4463
        return kwargs
1✔
4464

4465
    def set_kwargs(self, **kwargs):
1✔
4466
        """Set keyword argument injections.
4467

4468
        Existing keyword argument injections are dropped.
4469

4470
        :return: Reference ``self``
4471
        """
4472
        self._kwargs = parse_named_injections(kwargs)
1✔
4473
        self._kwargs_len = len(self._kwargs)
1✔
4474
        return self
1✔
4475

4476
    @property
4477
    def related(self):
4478
        """Return related providers generator."""
4479
        if is_provider(self.provides):
1✔
4480
            yield self.provides
1✔
4481
        yield from filter(is_provider, self.args)
1✔
4482
        yield from filter(is_provider, self.kwargs.values())
1✔
4483
        yield from super().related
1✔
4484

4485
    cpdef object _provide(self, tuple args, dict kwargs):
1✔
4486
        call = self.provides()
1✔
4487
        if __is_future_or_coroutine(call):
1✔
4488
            future_result = asyncio.Future()
1✔
4489
            call = asyncio.ensure_future(call)
1✔
4490
            call.add_done_callback(functools.partial(self._async_provide, future_result, args, kwargs))
1✔
4491
            return future_result
1✔
4492
        return __call(
1✔
4493
            call,
4494
            args,
4495
            self._args,
1✔
4496
            self._args_len,
4497
            kwargs,
4498
            self._kwargs,
1✔
4499
            self._kwargs_len,
4500
            self._async_mode,
4501
        )
4502

4503
    def _async_provide(self, future_result, args, kwargs, future):
1✔
4504
        try:
1✔
4505
            call = future.result()
1✔
4506
            result = __call(
1✔
4507
                call,
4508
                args,
1✔
4509
                self._args,
1✔
4510
                self._args_len,
4511
                kwargs,
1✔
4512
                self._kwargs,
1✔
4513
                self._kwargs_len,
4514
                self._async_mode,
4515
            )
4516
        except Exception as exception:
1✔
4517
            future_result.set_exception(exception)
1✔
4518
        else:
4519
            future_result.set_result(result)
1✔
4520

4521

4522
cdef class Injection:
4523
    """Abstract injection class."""
4524

4525

4526
cdef class PositionalInjection(Injection):
4527
    """Positional injection class."""
4528

4529
    def __init__(self, value=None):
4530
        """Initializer."""
4531
        self._value = None
1✔
4532
        self._is_provider = 0
1✔
4533
        self._is_delegated = 0
1✔
4534
        self._call = 0
1✔
4535
        self.set(value)
1✔
4536
        super(PositionalInjection, self).__init__()
1✔
4537

4538
    def __deepcopy__(self, memo):
1✔
4539
        """Create and return full copy of provider."""
4540
        copied = memo.get(id(self))
1✔
4541
        if copied is not None:
1✔
4542
            return copied
×
4543
        copied = _memorized_duplicate(self, memo)
1✔
4544
        copied.set(_copy_if_provider(self._value, memo))
1✔
4545
        return copied
1✔
4546

4547
    def get_value(self):
1✔
4548
        """Return injection value."""
4549
        return __get_value(self)
1✔
4550

4551
    def get_original_value(self):
1✔
4552
        """Return original value."""
4553
        return self._value
1✔
4554

4555
    def set(self, value):
1✔
4556
        """Set injection."""
4557
        self._value = value
1✔
4558
        self._is_provider = <int>is_provider(value)
1✔
4559
        self._is_delegated = <int>is_delegated(value)
1✔
4560
        self._call = <int>(self._is_provider == 1 and self._is_delegated == 0)
1✔
4561

4562

4563
cdef class NamedInjection(Injection):
4564
    """Keyword injection class."""
4565

4566
    def __init__(self, name=None, value=None):
4567
        """Initializer."""
4568
        self._name = name
1✔
4569
        self.set_name(name)
1✔
4570

4571
        self._value = None
1✔
4572
        self._is_provider = 0
1✔
4573
        self._is_delegated = 0
1✔
4574
        self._call = 0
1✔
4575
        self.set(value)
1✔
4576

4577
        super(NamedInjection, self).__init__()
1✔
4578

4579
    def __deepcopy__(self, memo):
1✔
4580
        """Create and return full copy of provider."""
4581
        copied = memo.get(id(self))
1✔
4582
        if copied is not None:
1✔
4583
            return copied
×
4584
        copied = _memorized_duplicate(self, memo)
1✔
4585
        copied.set_name(self.get_name())
1✔
4586
        copied.set(_copy_if_provider(self._value, memo))
1✔
4587
        return copied
1✔
4588

4589
    def get_name(self):
1✔
4590
        """Return injection name."""
4591
        return __get_name(self)
1✔
4592

4593
    def set_name(self, name):
1✔
4594
        """Set injection name."""
4595
        self._name = name
1✔
4596

4597
    def get_value(self):
1✔
4598
        """Return injection value."""
4599
        return __get_value(self)
1✔
4600

4601
    def get_original_value(self):
1✔
4602
        """Return original value."""
4603
        return self._value
1✔
4604

4605
    def set(self, value):
1✔
4606
        """Set injection."""
4607
        self._value = value
1✔
4608
        self._is_provider = <int>is_provider(value)
1✔
4609
        self._is_delegated = <int>is_delegated(value)
1✔
4610
        self._call = <int>(self._is_provider == 1 and self._is_delegated == 0)
1✔
4611

4612

4613
@cython.boundscheck(False)
1✔
4614
@cython.wraparound(False)
4615
cpdef tuple parse_positional_injections(tuple args):
4616
    """Parse positional injections."""
4617
    cdef list injections = list()
1✔
4618
    cdef int args_len = len(args)
1✔
4619

4620
    cdef int index
4621
    cdef object arg
4622
    cdef PositionalInjection injection
4623

4624
    for index in range(args_len):
1✔
4625
        arg = args[index]
1✔
4626
        injection = PositionalInjection(arg)
1✔
4627
        injections.append(injection)
1✔
4628

4629
    return tuple(injections)
1✔
4630

4631

4632
@cython.boundscheck(False)
1✔
4633
@cython.wraparound(False)
4634
cpdef tuple parse_named_injections(dict kwargs):
4635
    """Parse named injections."""
4636
    cdef list injections = list()
1✔
4637

4638
    cdef object name
4639
    cdef object arg
4640
    cdef NamedInjection injection
4641

4642
    for name, arg in kwargs.items():
1✔
4643
        injection = NamedInjection(name, arg)
1✔
4644
        injections.append(injection)
1✔
4645

4646
    return tuple(injections)
1✔
4647

4648

4649
cdef class OverridingContext:
4650
    """Provider overriding context.
4651

4652
    :py:class:`OverridingContext` is used by :py:meth:`Provider.override` for
4653
    implementing ``with`` contexts. When :py:class:`OverridingContext` is
4654
    closed, overriding that was created in this context is dropped also.
4655

4656
    .. code-block:: python
4657

4658
        with provider.override(another_provider):
4659
            assert provider.overridden
4660
        assert not provider.overridden
4661
    """
4662

4663
    def __init__(self, Provider overridden, Provider overriding):
4664
        """Initializer.
4665

4666
        :param overridden: Overridden provider.
4667
        :type overridden: :py:class:`Provider`
4668

4669
        :param overriding: Overriding provider.
4670
        :type overriding: :py:class:`Provider`
4671
        """
4672
        self._overridden = overridden
1✔
4673
        self._overriding = overriding
1✔
4674
        super(OverridingContext, self).__init__()
1✔
4675

4676
    def __enter__(self):
1✔
4677
        """Do nothing."""
4678
        return self._overriding
1✔
4679

4680
    def __exit__(self, *_):
1✔
4681
        """Exit overriding context."""
4682
        self._overridden.reset_last_overriding()
1✔
4683

4684

4685
cdef class BaseSingletonResetContext:
4686

4687
    def __init__(self, Provider provider):
4688
        self._singleton = provider
1✔
4689
        super().__init__()
1✔
4690

4691
    def __enter__(self):
1✔
4692
        return self._singleton
1✔
4693

4694
    def __exit__(self, *_):
1✔
4695
        raise NotImplementedError()
×
4696

4697

4698
cdef class SingletonResetContext(BaseSingletonResetContext):
4699

4700
    def __exit__(self, *_):
1✔
4701
        return self._singleton.reset()
1✔
4702

4703

4704
cdef class SingletonFullResetContext(BaseSingletonResetContext):
4705

4706
    def __exit__(self, *_):
1✔
4707
        return self._singleton.full_reset()
1✔
4708

4709

4710
CHILD_PROVIDERS = (Dependency, DependenciesContainer, Container)
1✔
4711

4712

4713
cpdef bint is_provider(object instance):
1✔
4714
    """Check if instance is provider instance.
4715

4716
    :param instance: Instance to be checked.
4717
    :type instance: object
4718

4719
    :rtype: bool
4720
    """
4721
    return (not isinstance(instance, type) and
1✔
4722
            getattr(instance, "__IS_PROVIDER__", False) is True)
1✔
4723

4724

4725
cpdef object ensure_is_provider(object instance):
1✔
4726
    """Check if instance is provider instance and return it.
4727

4728
    :param instance: Instance to be checked.
4729
    :type instance: object
4730

4731
    :raise: :py:exc:`dependency_injector.errors.Error` if provided instance is
4732
            not provider.
4733

4734
    :rtype: :py:class:`dependency_injector.providers.Provider`
4735
    """
4736
    if not is_provider(instance):
1✔
4737
        raise Error("Expected provider instance, got {0}".format(str(instance)))
1✔
4738
    return instance
1✔
4739

4740

4741
cpdef bint is_delegated(object instance):
1✔
4742
    """Check if instance is delegated provider.
4743

4744
    :param instance: Instance to be checked.
4745
    :type instance: object
4746

4747
    :rtype: bool
4748
    """
4749
    return (not isinstance(instance, type) and
1✔
4750
            getattr(instance, "__IS_DELEGATED__", False) is True)
1✔
4751

4752

4753
cpdef str represent_provider(object provider, object provides):
1✔
4754
    """Return string representation of provider.
4755

4756
    :param provider: Provider object
4757
    :type provider: :py:class:`dependency_injector.providers.Provider`
4758

4759
    :param provides: Object that provider provides
4760
    :type provider: object
4761

4762
    :return: String representation of provider
4763
    :rtype: str
4764
    """
4765
    return "<{provider}({provides}) at {address}>".format(
1✔
4766
        provider=".".join((provider.__class__.__module__,
1✔
4767
                           provider.__class__.__name__)),
1✔
4768
        provides=repr(provides) if provides is not None else "",
1✔
4769
        address=hex(id(provider)))
1✔
4770

4771

4772
cpdef bint is_container_instance(object instance):
1✔
4773
    """Check if instance is container instance.
4774

4775
    :param instance: Instance to be checked.
4776
    :type instance: object
4777

4778
    :rtype: bool
4779
    """
4780
    return (not isinstance(instance, type) and
1✔
4781
            getattr(instance, "__IS_CONTAINER__", False) is True)
1✔
4782

4783

4784
cpdef bint is_container_class(object instance):
1✔
4785
    """Check if instance is container class.
4786

4787
    :param instance: Instance to be checked.
4788
    :type instance: object
4789

4790
    :rtype: bool
4791
    """
4792
    return (isinstance(instance, type) and
×
4793
            getattr(instance, "__IS_CONTAINER__", False) is True)
×
4794

4795

4796
cpdef object deepcopy(object instance, dict memo=None):
1✔
4797
    """Return full copy of provider or container with providers."""
4798
    if memo is None:
1✔
4799
        memo = dict()
1✔
4800

4801
    __add_sys_streams(memo)
1✔
4802

4803
    return copy.deepcopy(instance, memo)
1✔
4804

4805

4806
cpdef tuple deepcopy_args(
1✔
4807
    Provider provider,
4808
    tuple args,
4809
    dict[int, object] memo = None,
1✔
4810
):
4811
    """A wrapper for deepcopy for positional arguments.
4812

4813
    Used to improve debugability of objects that cannot be deep-copied.
4814
    """
4815

4816
    cdef list[object] out = []
1✔
4817

4818
    for i, arg in enumerate(args):
1✔
4819
        try:
1✔
4820
            out.append(copy.deepcopy(arg, memo))
1✔
4821
        except Exception as e:
1✔
4822
            raise NonCopyableArgumentError(provider, index=i) from e
1✔
4823

4824
    return tuple(out)
1✔
4825

4826

4827
cpdef dict[str, object] deepcopy_kwargs(
1✔
4828
    Provider provider,
4829
    dict[str, object] kwargs,
4830
    dict[int, object] memo = None,
1✔
4831
):
4832
    """A wrapper for deepcopy for keyword arguments.
4833

4834
    Used to improve debugability of objects that cannot be deep-copied.
4835
    """
4836

4837
    cdef dict[str, object] out = {}
1✔
4838

4839
    for name, arg in kwargs.items():
1✔
4840
        try:
1✔
4841
            out[name] = copy.deepcopy(arg, memo)
1✔
4842
        except Exception as e:
1✔
4843
            raise NonCopyableArgumentError(provider, keyword=name) from e
1✔
4844

4845
    return out
1✔
4846

4847

4848
def __add_sys_streams(memo):
1✔
4849
    """Add system streams to memo dictionary.
4850

4851
    This helps to avoid copying of system streams while making a deepcopy of
4852
    objects graph.
4853
    """
4854
    memo[id(sys.stdin)] = sys.stdin
1✔
4855
    memo[id(sys.stdout)] = sys.stdout
1✔
4856
    memo[id(sys.stderr)] = sys.stderr
1✔
4857

4858

4859
def merge_dicts(dict1, dict2):
1✔
4860
    """Merge dictionaries recursively.
4861

4862
    :param dict1: Dictionary 1
4863
    :type dict1: dict
4864

4865
    :param dict2: Dictionary 2
4866
    :type dict2: dict
4867

4868
    :return: New resulting dictionary
4869
    :rtype: dict
4870
    """
4871
    for key, value in dict1.items():
1✔
4872
        if key in dict2:
1✔
4873
            if isinstance(value, dict) and isinstance(dict2[key], dict):
1✔
4874
                dict2[key] = merge_dicts(value, dict2[key])
1✔
4875
    result = dict1.copy()
1✔
4876
    result.update(dict2)
1✔
4877
    return result
1✔
4878

4879

4880
def traverse(*providers, types=None):
1✔
4881
    """Return providers traversal generator."""
4882
    visited = set()
1✔
4883
    to_visit = set(providers)
1✔
4884

4885
    if types:
1✔
4886
        types = tuple(types)
1✔
4887

4888
    while len(to_visit) > 0:
1✔
4889
        visiting = to_visit.pop()
1✔
4890
        visited.add(visiting)
1✔
4891

4892
        for child in visiting.related:
1✔
4893
            if child in visited:
1✔
4894
                continue
1✔
4895
            to_visit.add(child)
1✔
4896

4897
        if types and not isinstance(visiting, types):
1✔
4898
            continue
1✔
4899

4900
        yield visiting
1✔
4901

4902

4903
def isawaitable(obj):
1✔
4904
    """Check if object is a coroutine function."""
4905
    try:
×
4906
        return inspect.isawaitable(obj)
×
4907
    except AttributeError:
×
4908
        return False
×
4909

4910

4911
def iscoroutinefunction(obj):
1✔
4912
    """Check if object is a coroutine function."""
4913
    try:
×
4914
        return inspect.iscoroutinefunction(obj)
×
4915
    except AttributeError:
×
4916
        return False
×
4917

4918

4919
def _resolve_string_import(provides):
1✔
4920
    if provides is None:
1✔
4921
        return provides
1✔
4922

4923
    if not isinstance(provides, str):
1✔
4924
        return provides
1✔
4925

4926
    segments = provides.split(".")
1✔
4927
    member_name = segments[-1]
1✔
4928

4929
    if len(segments) == 1:
1✔
4930
        if  member_name in dir(builtins):
1✔
4931
            module = builtins
1✔
4932
        else:
4933
            module = _resolve_calling_module()
1✔
4934
        return getattr(module, member_name)
1✔
4935

4936
    module_name = ".".join(segments[:-1])
1✔
4937

4938
    package_name = _resolve_calling_package_name()
1✔
4939
    if module_name.startswith(".") and package_name is None:
1✔
4940
        raise ImportError("Attempted relative import with no known parent package")
×
4941

4942
    module = importlib.import_module(module_name, package=package_name)
1✔
4943
    return getattr(module, member_name)
1✔
4944

4945

4946
def _resolve_calling_module():
1✔
4947
    stack = inspect.stack()
1✔
4948
    pre_last_frame = stack[0]
1✔
4949
    return inspect.getmodule(pre_last_frame[0])
1✔
4950

4951

4952
def _resolve_calling_package_name():
1✔
4953
    module = _resolve_calling_module()
1✔
4954
    return module.__package__
1✔
4955

4956

4957
cpdef _copy_parent(object from_, object to, dict memo):
1✔
4958
    """Copy and assign provider parent."""
4959
    copied_parent = (
4960
        deepcopy(from_.parent, memo)
1✔
4961
        if is_provider(from_.parent) or is_container_instance(from_.parent)
1✔
4962
        else from_.parent
1✔
4963
    )
4964
    to.assign_parent(copied_parent)
1✔
4965

4966

4967
cpdef object _memorized_duplicate(object instance, dict memo):
1✔
4968
    copied = instance.__class__()
1✔
4969
    memo[id(instance)] = copied
1✔
4970
    return copied
1✔
4971

4972

4973
cpdef object _copy_if_provider(object instance, dict memo):
1✔
4974
    if not is_provider(instance):
1✔
4975
        return instance
1✔
4976
    return deepcopy(instance, memo)
1✔
4977

4978

4979
cpdef str _class_qualname(object instance):
1✔
4980
    name = getattr(instance.__class__, "__qualname__", None)
1✔
4981
    if not name:
1✔
4982
        name = ".".join((instance.__class__.__module__, instance.__class__.__name__))
×
4983
    return name
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