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

ets-labs / python-dependency-injector / 15354606512

30 May 2025 07:50PM UTC coverage: 94.635% (-0.03%) from 94.663%
15354606512

push

github

ZipFile
Bump version

3369 of 3560 relevant lines covered (94.63%)

0.95 hits per line

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

96.24
/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 configparser import ConfigParser as IniConfigParser
1✔
19
from contextvars import ContextVar
1✔
20

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

30
try:
1✔
31
    from asyncio.coroutines import _is_coroutine
1✔
32
except ImportError:
1✔
33
    _is_coroutine = True
×
34

35
try:
1✔
36
    import yaml
1✔
37
except ImportError:
×
38
    yaml = None
×
39

40
has_pydantic_settings = True
1✔
41
cdef bint pydantic_v1 = False
1✔
42
cdef str pydantic_module = "pydantic_settings"
1✔
43
cdef str pydantic_extra = "pydantic2"
1✔
44

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

59

60
from .errors import (
1✔
61
    Error,
1✔
62
    NoSuchProviderError,
63
    NonCopyableArgumentError,
64
)
65

66
cimport cython
67

68

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

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

77
    for match in reversed(findings):
1✔
78
        env_name = match.group("name")
1✔
79
        has_default = match.group("separator") == ":"
1✔
80

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

87
        span_min, span_max = match.span()
1✔
88
        config_content = f"{config_content[:span_min]}{value}{config_content[span_max:]}"
1✔
89
    return config_content
1✔
90

91

92
cdef object _parse_ini_file(filepath, envs_required: bool | None):
1✔
93
    parser = IniConfigParser()
1✔
94

95
    with open(filepath) as config_file:
1✔
96
        config_string = config_file.read()
1✔
97

98
        if envs_required is not None:
1✔
99
            config_string = _resolve_config_env_markers(
1✔
100
                config_string,
1✔
101
                envs_required=envs_required,
102
            )
103
    parser.read_string(config_string)
1✔
104
    return parser
1✔
105

106

107
if yaml:
1✔
108
    class YamlLoader(yaml.SafeLoader):
1✔
109
        """YAML loader.
110

111
        This loader mimics ``yaml.SafeLoader``.
112
        """
113
else:
114
    class YamlLoader:
×
115
        """YAML loader.
116

117
        This loader mimics ``yaml.SafeLoader``.
118
        """
119

120

121
UNDEFINED = object()
1✔
122

123
cdef int ASYNC_MODE_UNDEFINED = 0
1✔
124
cdef int ASYNC_MODE_ENABLED = 1
1✔
125
cdef int ASYNC_MODE_DISABLED = 2
1✔
126

127
cdef set __iscoroutine_typecache = set()
1✔
128
cdef tuple __COROUTINE_TYPES = asyncio.coroutines._COROUTINE_TYPES
1✔
129

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

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

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

150
    if pydantic_v1:
1✔
151
        return settings.dict(**kwargs)
×
152

153
    return settings.model_dump(mode="python", **kwargs)
1✔
154

155

156
cdef class Provider:
157
    """Base provider class.
158

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

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

169
    :py:class:`Provider` implements provider overriding logic that should be
170
    also common for all providers:
171

172
    .. code-block:: python
173

174
        provider1 = Factory(SomeClass)
175
        provider2 = Factory(ChildSomeClass)
176

177
        provider1.override(provider2)
178

179
        some_instance = provider1()
180
        assert isinstance(some_instance, ChildSomeClass)
181

182
    Also :py:class:`Provider` implements helper function for creating its
183
    delegates:
184

185
    .. code-block:: python
186

187
        provider = Factory(object)
188
        delegate = provider.delegate()
189

190
        delegated = delegate()
191

192
        assert provider is delegated
193

194
    All providers should extend this class.
195

196
    .. py:attribute:: overridden
197
       :noindex:
198

199
        Tuple of overriding providers, if any.
200

201
        :type: tuple[:py:class:`Provider`] | None
202
    """
203

204
    __IS_PROVIDER__ = True
1✔
205

206
    overriding_lock = threading.RLock()
1✔
207
    """Overriding reentrant lock.
208

209
    :type: :py:class:`threading.RLock`
210
    """
211

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

220
    def __call__(self, *args, **kwargs):
221
        """Return provided object.
222

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

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

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

249
        copied = _memorized_duplicate(self, memo)
1✔
250
        self._copy_overridings(copied, memo)
1✔
251
        return copied
1✔
252

253
    @classmethod
1✔
254
    def __class_getitem__(cls, item):
255
        return cls
1✔
256

257
    def __str__(self):
258
        """Return string representation of provider.
259

260
        :rtype: str
261
        """
262
        return represent_provider(provider=self, provides=None)
1✔
263

264
    def __repr__(self):
265
        """Return string representation of provider.
266

267
        :rtype: str
268
        """
269
        return self.__str__()
1✔
270

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

277
    @property
278
    def last_overriding(self):
279
        """Return last overriding provider.
280

281
        If provider is not overridden, then None is returned.
282
        """
283
        return self._last_overriding
1✔
284

285
    def override(self, provider):
1✔
286
        """Override provider with another provider.
287

288
        :param provider: Overriding provider.
289
        :type provider: :py:class:`Provider`
290

291
        :raise: :py:exc:`dependency_injector.errors.Error`
292

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

299
        if not is_provider(provider):
1✔
300
            provider = Object(provider)
1✔
301

302
        with self.overriding_lock:
1✔
303
            self._overridden += (provider,)
1✔
304
            self._last_overriding = provider
1✔
305
            provider.register_overrides(self)
1✔
306

307
        return OverridingContext(self, provider)
1✔
308

309
    def reset_last_overriding(self):
1✔
310
        """Reset last overriding provider.
311

312
        :raise: :py:exc:`dependency_injector.errors.Error` if provider is not
313
                overridden.
314

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

321
            self._last_overriding.unregister_overrides(self)
1✔
322

323
            self._overridden = self._overridden[:-1]
1✔
324
            try:
1✔
325
                self._last_overriding = self._overridden[-1]
1✔
326
            except IndexError:
1✔
327
                self._last_overriding = None
1✔
328

329
    def reset_override(self):
1✔
330
        """Reset all overriding providers.
331

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

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

345
    def register_overrides(self, provider):
1✔
346
        """Register provider that overrides current provider."""
347
        self._overrides =  tuple(set(self._overrides + (provider,)))
1✔
348

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

356
    def async_(self, *args, **kwargs):
1✔
357
        """Return provided object asynchronously.
358

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

363
        .. code-block:: python
364

365
            database_provider: Provider[DatabaseConnection] = Resource(init_db_async)
366

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

373
    def delegate(self):
1✔
374
        """Return provider delegate.
375

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

385
    @property
386
    def provider(self):
387
        """Return provider"s delegate.
388

389
        :rtype: :py:class:`Delegate`
390
        """
391
        return Delegate(self)
1✔
392

393
    @property
394
    def provided(self):
395
        """Return :py:class:`ProvidedInstance` provider."""
396
        return ProvidedInstance(self)
1✔
397

398
    def enable_async_mode(self):
1✔
399
        """Enable async mode."""
400
        self._async_mode = ASYNC_MODE_ENABLED
1✔
401

402
    def disable_async_mode(self):
1✔
403
        """Disable async mode."""
404
        self._async_mode = ASYNC_MODE_DISABLED
1✔
405

406
    def reset_async_mode(self):
1✔
407
        """Reset async mode.
408

409
        Provider will automatically set the mode on the next call.
410
        """
411
        self._async_mode = ASYNC_MODE_UNDEFINED
1✔
412

413
    cpdef bint is_async_mode_enabled(self):
1✔
414
        """Check if async mode is enabled."""
415
        return self._async_mode == ASYNC_MODE_ENABLED
1✔
416

417
    cpdef bint is_async_mode_disabled(self):
1✔
418
        """Check if async mode is disabled."""
419
        return self._async_mode == ASYNC_MODE_DISABLED
1✔
420

421
    cpdef bint is_async_mode_undefined(self):
1✔
422
        """Check if async mode is undefined."""
423
        return self._async_mode == ASYNC_MODE_UNDEFINED
1✔
424

425
    @property
426
    def related(self):
427
        """Return related providers generator."""
428
        yield from self.overridden
1✔
429

430
    def traverse(self, types=None):
1✔
431
        """Return providers traversal generator."""
432
        return traverse(*self.related, types=types)
1✔
433

434
    cpdef object _provide(self, tuple args, dict kwargs):
1✔
435
        """Providing strategy implementation.
436

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

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

449

450
cdef class Object(Provider):
451
    """Object provider returns provided instance "as is".
452

453
    .. py:attribute:: provides
454

455
        Value that have to be provided.
456

457
        :type: object
458
    """
459

460
    def __init__(self, provides=None):
461
        """Initialize provider."""
462
        self._provides = None
1✔
463
        self.set_provides(provides)
1✔
464
        super(Object, self).__init__()
1✔
465

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

472
        copied = _memorized_duplicate(self, memo)
1✔
473
        copied.set_provides(self.provides)
1✔
474

475
        self._copy_overridings(copied, memo)
1✔
476

477
        return copied
1✔
478

479
    def __str__(self):
480
        """Return string representation of provider.
481

482
        :rtype: str
483
        """
484
        return represent_provider(provider=self, provides=self._provides)
1✔
485

486
    def __repr__(self):
487
        """Return string representation of provider.
488

489
        :rtype: str
490
        """
491
        return self.__str__()
1✔
492

493
    @property
494
    def provides(self):
495
        """Return provider provides."""
496
        return self._provides
1✔
497

498
    def set_provides(self, provides):
1✔
499
        """Set provider provides."""
500
        self._provides = provides
1✔
501
        return self
1✔
502

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

510
    cpdef object _provide(self, tuple args, dict kwargs):
1✔
511
        """Return provided instance.
512

513
        :param args: Tuple of context positional arguments.
514
        :type args: tuple[object]
515

516
        :param kwargs: Dictionary of context keyword arguments.
517
        :type kwargs: dict[str, object]
518

519
        :rtype: object
520
        """
521
        return self._provides
1✔
522

523

524
cdef class Self(Provider):
525
    """Self provider returns own container."""
526

527
    def __init__(self, container=None):
528
        """Initialize provider."""
529
        self._container = container
1✔
530
        self._alt_names = tuple()
1✔
531
        super().__init__()
1✔
532

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

539
        copied = _memorized_duplicate(self, memo)
1✔
540
        copied.set_container(deepcopy(self._container, memo))
1✔
541
        copied.set_alt_names(self._alt_names)
1✔
542
        self._copy_overridings(copied, memo)
1✔
543
        return copied
1✔
544

545
    def __str__(self):
546
        """Return string representation of provider.
547

548
        :rtype: str
549
        """
550
        return represent_provider(provider=self, provides=self._container)
1✔
551

552
    def __repr__(self):
553
        """Return string representation of provider.
554

555
        :rtype: str
556
        """
557
        return self.__str__()
1✔
558

559
    def set_container(self, container):
1✔
560
        self._container = container
1✔
561

562
    def set_alt_names(self, alt_names):
1✔
563
        self._alt_names = tuple(set(alt_names))
1✔
564

565
    @property
566
    def alt_names(self):
567
        return self._alt_names
1✔
568

569
    cpdef object _provide(self, tuple args, dict kwargs):
1✔
570
        return self._container
1✔
571

572

573
cdef class Delegate(Provider):
574
    """Delegate provider returns provider "as is".
575

576
    .. py:attribute:: provides
577

578
        Value that have to be provided.
579

580
        :type: object
581
    """
582

583
    def __init__(self, provides=None):
584
        """Initialize provider."""
585
        self._provides = None
1✔
586
        self.set_provides(provides)
1✔
587
        super(Delegate, self).__init__()
1✔
588

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

595
        copied = _memorized_duplicate(self, memo)
1✔
596
        copied.set_provides(_copy_if_provider(self.provides, memo))
1✔
597
        self._copy_overridings(copied, memo)
1✔
598

599
        return copied
1✔
600

601
    def __str__(self):
602
        """Return string representation of provider.
603

604
        :rtype: str
605
        """
606
        return represent_provider(provider=self, provides=self._provides)
1✔
607

608
    def __repr__(self):
609
        """Return string representation of provider.
610

611
        :rtype: str
612
        """
613
        return self.__str__()
1✔
614

615
    @property
616
    def provides(self):
617
        """Return provider provides."""
618
        return self._provides
1✔
619

620
    def set_provides(self, provides):
1✔
621
        """Set provider provides."""
622
        if provides:
1✔
623
            provides = ensure_is_provider(provides)
1✔
624
        self._provides = provides
1✔
625
        return self
1✔
626

627
    @property
628
    def related(self):
629
        """Return related providers generator."""
630
        yield self._provides
1✔
631
        yield from super().related
1✔
632

633
    cpdef object _provide(self, tuple args, dict kwargs):
1✔
634
        """Return provided instance.
635

636
        :param args: Tuple of context positional arguments.
637
        :type args: tuple[object]
638

639
        :param kwargs: Dictionary of context keyword arguments.
640
        :type kwargs: dict[str, object]
641

642
        :rtype: object
643
        """
644
        return self._provides
1✔
645

646

647
cdef class Aggregate(Provider):
648
    """Providers aggregate.
649

650
    :py:class:`Aggregate` is a delegated provider, meaning that it is
651
    injected "as is".
652

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

658
    __IS_DELEGATED__ = True
1✔
659

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

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

672
        copied = _memorized_duplicate(self, memo)
1✔
673
        copied.set_providers(deepcopy(self.providers, memo))
1✔
674

675
        self._copy_overridings(copied, memo)
1✔
676

677
        return copied
1✔
678

679
    def __getattr__(self, factory_name):
680
        """Return aggregated provider."""
681
        return self.__get_provider(factory_name)
1✔
682

683
    def __str__(self):
684
        """Return string representation of provider.
685

686
        :rtype: str
687
        """
688
        return represent_provider(provider=self, provides=self.providers)
1✔
689

690
    @property
691
    def providers(self):
692
        """Return dictionary of providers, read-only.
693

694
        Alias for ``.factories`` attribute.
695
        """
696
        return dict(self._providers)
1✔
697

698
    def set_providers(self, provider_dict=None, **provider_kwargs):
1✔
699
        """Set providers.
700

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

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

718
        self._providers = providers
1✔
719
        return self
1✔
720

721
    def override(self, _):
1✔
722
        """Override provider with another provider.
723

724
        :raise: :py:exc:`dependency_injector.errors.Error`
725

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

731
    @property
732
    def related(self):
733
        """Return related providers generator."""
734
        yield from self._providers.values()
1✔
735
        yield from super().related
1✔
736

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

748
        return self.__get_provider(provider_name)(*args, **kwargs)
1✔
749

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

755

756
cdef class Dependency(Provider):
757
    """:py:class:`Dependency` provider describes dependency interface.
758

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

764
    .. code-block:: python
765

766
        database_provider = Dependency(sqlite3.dbapi2.Connection)
767
        database_provider.override(Factory(sqlite3.connect, ":memory:"))
768

769
        database = database_provider()
770

771
    .. py:attribute:: instance_of
772
       :noindex:
773

774
        Class of required dependency.
775

776
        :type: type
777
   """
778

779
    def __init__(self, object instance_of=object, default=None):
780
        """Initialize provider."""
781
        self._instance_of = None
1✔
782
        self.set_instance_of(instance_of)
1✔
783

784
        self._default = None
1✔
785
        self.set_default(default)
1✔
786

787
        self._parent = None
1✔
788

789
        super(Dependency, self).__init__()
1✔
790

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

797
        copied = _memorized_duplicate(self, memo)
1✔
798
        copied.set_instance_of(self.instance_of)
1✔
799
        copied.set_default(deepcopy(self.default, memo))
1✔
800

801
        self._copy_parent(copied, memo)
1✔
802
        self._copy_overridings(copied, memo)
1✔
803

804
        return copied
1✔
805

806
    def __call__(self, *args, **kwargs):
807
        """Return provided instance.
808

809
        :raise: :py:exc:`dependency_injector.errors.Error`
810

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

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

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

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

852
    def __str__(self):
853
        """Return string representation of provider.
854

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

864
    def __repr__(self):
865
        """Return string representation of provider.
866

867
        :rtype: str
868
        """
869
        return self.__str__()
1✔
870

871
    @property
872
    def instance_of(self):
873
        """Return type."""
874
        return self._instance_of
1✔
875

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

885
    @property
886
    def default(self):
887
        """Return default provider."""
888
        return self._default
1✔
889

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

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

902
    def provided_by(self, provider):
1✔
903
        """Set external dependency provider.
904

905
        :param provider: Provider that provides required dependency.
906
        :type provider: :py:class:`Provider`
907

908
        :rtype: None
909
        """
910
        return self.override(provider)
1✔
911

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

919
    @property
920
    def parent(self):
921
        """Return parent."""
922
        return self._parent
1✔
923

924
    @property
925
    def parent_name(self):
926
        """Return parent name."""
927
        if not self._parent:
1✔
928
            return None
1✔
929

930
        name = ""
1✔
931
        if self._parent.parent_name:
1✔
932
            name += f"{self._parent.parent_name}."
1✔
933
        name += f"{self._parent.resolve_provider_name(self)}"
1✔
934

935
        return name
1✔
936

937
    def assign_parent(self, parent):
1✔
938
        """Assign parent."""
939
        self._parent = parent
1✔
940

941
    def _copy_parent(self, copied, memo):
1✔
942
        _copy_parent(self, copied, memo)
1✔
943

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

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

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

962

963
cdef class ExternalDependency(Dependency):
964
    """:py:class:`ExternalDependency` provider describes dependency interface.
965

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

971
    .. code-block:: python
972

973
        database_provider = ExternalDependency(sqlite3.dbapi2.Connection)
974
        database_provider.override(Factory(sqlite3.connect, ":memory:"))
975

976
        database = database_provider()
977

978
    .. deprecated:: 3.9
979

980
        Use :py:class:`Dependency` instead.
981

982
    .. py:attribute:: instance_of
983
       :noindex:
984

985
        Class of required dependency.
986

987
        :type: type
988
    """
989

990

991
cdef class DependenciesContainer(Object):
992
    """:py:class:`DependenciesContainer` provider provides set of dependencies.
993

994

995
    Dependencies container provider is used to implement late static binding
996
    for a set of providers of a particular container.
997

998
    Example code:
999

1000
    .. code-block:: python
1001

1002
        class Adapters(containers.DeclarativeContainer):
1003
            email_sender = providers.Singleton(SmtpEmailSender)
1004

1005
        class TestAdapters(containers.DeclarativeContainer):
1006
            email_sender = providers.Singleton(EchoEmailSender)
1007

1008
        class UseCases(containers.DeclarativeContainer):
1009
            adapters = providers.DependenciesContainer()
1010

1011
            signup = providers.Factory(SignupUseCase,
1012
                                       email_sender=adapters.email_sender)
1013

1014
        use_cases = UseCases(adapters=Adapters)
1015
        # or
1016
        use_cases = UseCases(adapters=TestAdapters)
1017

1018
        # Another file
1019
        from .containers import use_cases
1020

1021
        use_case = use_cases.signup()
1022
        use_case.execute()
1023
    """
1024

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

1031
        self._providers = dependencies
1✔
1032
        self._parent = None
1✔
1033

1034
        super(DependenciesContainer, self).__init__(None)
1✔
1035

1036
    def __deepcopy__(self, memo):
1✔
1037
        """Create and return full copy of provider."""
1038
        cdef DependenciesContainer copied
1039

1040
        copied = memo.get(id(self))
1✔
1041
        if copied is not None:
1✔
1042
            return copied
×
1043

1044
        copied = <DependenciesContainer> _memorized_duplicate(self, memo)
1✔
1045
        copied._provides = deepcopy(self._provides, memo)
1✔
1046
        copied._providers = deepcopy(self._providers, memo)
1✔
1047
        self._copy_parent(copied, memo)
1✔
1048
        self._copy_overridings(copied, memo)
1✔
1049

1050
        return copied
1✔
1051

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

1060
        provider = self._providers.get(name)
1✔
1061
        if not provider:
1✔
1062
            provider = Dependency()
1✔
1063
            provider.assign_parent(self)
1✔
1064

1065
            self._providers[name] = provider
1✔
1066

1067
            container = self.__call__()
1✔
1068
            if container:
1✔
1069
                dependency_provider = container.providers.get(name)
1✔
1070
                if dependency_provider:
1✔
1071
                    provider.override(dependency_provider)
1✔
1072

1073
        return provider
1✔
1074

1075
    @property
1076
    def providers(self):
1077
        """Read-only dictionary of dependency providers."""
1078
        return self._providers
1✔
1079

1080
    def override(self, provider):
1✔
1081
        """Override provider with another provider.
1082

1083
        :param provider: Overriding provider.
1084
        :type provider: :py:class:`Provider`
1085

1086
        :raise: :py:exc:`dependency_injector.errors.Error`
1087

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

1094
    def reset_last_overriding(self):
1✔
1095
        """Reset last overriding provider.
1096

1097
        :raise: :py:exc:`dependency_injector.errors.Error` if provider is not
1098
                overridden.
1099

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

1109
    def reset_override(self):
1✔
1110
        """Reset all overriding providers.
1111

1112
        :rtype: None
1113
        """
1114
        for child in self._providers.values():
1✔
1115
            child.reset_override()
1✔
1116
        super(DependenciesContainer, self).reset_override()
1✔
1117

1118
    @property
1119
    def related(self):
1120
        """Return related providers generator."""
1121
        yield from self.providers.values()
1✔
1122
        yield from super().related
1✔
1123

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

1132
    @property
1133
    def parent(self):
1134
        """Return parent."""
1135
        return self._parent
1✔
1136

1137
    @property
1138
    def parent_name(self):
1139
        """Return parent name."""
1140
        if not self._parent:
1✔
1141
            return None
1✔
1142

1143
        name = ""
1✔
1144
        if self._parent.parent_name:
1✔
1145
            name += f"{self._parent.parent_name}."
1✔
1146
        name += f"{self._parent.resolve_provider_name(self)}"
1✔
1147

1148
        return name
1✔
1149

1150
    def assign_parent(self, parent):
1✔
1151
        """Assign parent."""
1152
        self._parent = parent
1✔
1153

1154
    def _copy_parent(self, copied, memo):
1✔
1155
        _copy_parent(self, copied, memo)
1✔
1156

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

1162
            if provider.last_overriding is dependency_provider:
1✔
1163
                continue
1✔
1164

1165
            provider.override(dependency_provider)
1✔
1166

1167

1168
cdef class Callable(Provider):
1169
    r"""Callable provider calls wrapped callable on every call.
1170

1171
    Callable supports positional and keyword argument injections:
1172

1173
    .. code-block:: python
1174

1175
        some_function = Callable(some_function,
1176
                                 "positional_arg1", "positional_arg2",
1177
                                 keyword_argument1=3, keyword_argument=4)
1178

1179
        # or
1180

1181
        some_function = Callable(some_function) \
1182
            .add_args("positional_arg1", "positional_arg2") \
1183
            .add_kwargs(keyword_argument1=3, keyword_argument=4)
1184

1185
        # or
1186

1187
        some_function = Callable(some_function)
1188
        some_function.add_args("positional_arg1", "positional_arg2")
1189
        some_function.add_kwargs(keyword_argument1=3, keyword_argument=4)
1190
    """
1191

1192
    def __init__(self, provides=None, *args, **kwargs):
1193
        """Initialize provider."""
1194
        self._provides = None
1✔
1195
        self.set_provides(provides)
1✔
1196

1197
        self._args = tuple()
1✔
1198
        self._args_len = 0
1✔
1199
        self.set_args(*args)
1✔
1200

1201
        self._kwargs = tuple()
1✔
1202
        self._kwargs_len = 0
1✔
1203
        self.set_kwargs(**kwargs)
1✔
1204

1205
        super(Callable, self).__init__()
1✔
1206

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

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

1220
    def __str__(self):
1221
        """Return string representation of provider.
1222

1223
        :rtype: str
1224
        """
1225
        return represent_provider(provider=self, provides=self._provides)
1✔
1226

1227
    @property
1228
    def provides(self):
1229
        """Return provider provides."""
1230
        return self._provides
1✔
1231

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

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

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

1258
    def add_args(self, *args):
1✔
1259
        """Add positional argument injections.
1260

1261
        :return: Reference ``self``
1262
        """
1263
        self._args += parse_positional_injections(args)
1✔
1264
        self._args_len = len(self._args)
1✔
1265
        return self
1✔
1266

1267
    def set_args(self, *args):
1✔
1268
        """Set positional argument injections.
1269

1270
        Existing positional argument injections are dropped.
1271

1272
        :return: Reference ``self``
1273
        """
1274
        self._args = parse_positional_injections(args)
1✔
1275
        self._args_len = len(self._args)
1✔
1276
        return self
1✔
1277

1278
    def clear_args(self):
1✔
1279
        """Drop positional argument injections.
1280

1281
        :return: Reference ``self``
1282
        """
1283
        self._args = tuple()
1✔
1284
        self._args_len = len(self._args)
1✔
1285
        return self
1✔
1286

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

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

1300
    def add_kwargs(self, **kwargs):
1✔
1301
        """Add keyword argument injections.
1302

1303
        :return: Reference ``self``
1304
        """
1305
        self._kwargs += parse_named_injections(kwargs)
1✔
1306
        self._kwargs_len = len(self._kwargs)
1✔
1307
        return self
1✔
1308

1309
    def set_kwargs(self, **kwargs):
1✔
1310
        """Set keyword argument injections.
1311

1312
        Existing keyword argument injections are dropped.
1313

1314
        :return: Reference ``self``
1315
        """
1316
        self._kwargs = parse_named_injections(kwargs)
1✔
1317
        self._kwargs_len = len(self._kwargs)
1✔
1318
        return self
1✔
1319

1320
    def clear_kwargs(self):
1✔
1321
        """Drop keyword argument injections.
1322

1323
        :return: Reference ``self``
1324
        """
1325
        self._kwargs = tuple()
1✔
1326
        self._kwargs_len = len(self._kwargs)
1✔
1327
        return self
1✔
1328

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

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

1341

1342
cdef class DelegatedCallable(Callable):
1343
    """Callable that is injected "as is".
1344

1345
    DelegatedCallable is a :py:class:`Callable`, that is injected "as is".
1346
    """
1347

1348
    __IS_DELEGATED__ = True
1✔
1349

1350

1351
cdef class AbstractCallable(Callable):
1352
    """Abstract callable provider.
1353

1354
    :py:class:`AbstractCallable` is a :py:class:`Callable` provider that must
1355
    be explicitly overridden before calling.
1356

1357
    Overriding of :py:class:`AbstractCallable` is possible only by another
1358
    :py:class:`Callable` provider.
1359
    """
1360

1361
    def __call__(self, *args, **kwargs):
1362
        """Return provided object.
1363

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

1370
    def override(self, provider):
1✔
1371
        """Override provider with another provider.
1372

1373
        :param provider: Overriding provider.
1374
        :type provider: :py:class:`Provider`
1375

1376
        :raise: :py:exc:`dependency_injector.errors.Error`
1377

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

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

1391

1392
cdef class CallableDelegate(Delegate):
1393
    """Callable delegate injects delegating callable "as is".
1394

1395
    .. py:attribute:: provides
1396

1397
        Value that have to be provided.
1398

1399
        :type: object
1400
    """
1401

1402
    def __init__(self, callable):
1403
        """Initializer.
1404

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

1412

1413
cdef class Coroutine(Callable):
1414
    r"""Coroutine provider creates wrapped coroutine on every call.
1415

1416
    Coroutine supports positional and keyword argument injections:
1417

1418
    .. code-block:: python
1419

1420
        some_coroutine = Coroutine(some_coroutine,
1421
                                   "positional_arg1", "positional_arg2",
1422
                                   keyword_argument1=3, keyword_argument=4)
1423

1424
        # or
1425

1426
        some_coroutine = Coroutine(some_coroutine) \
1427
            .add_args("positional_arg1", "positional_arg2") \
1428
            .add_kwargs(keyword_argument1=3, keyword_argument=4)
1429

1430
        # or
1431

1432
        some_coroutine = Coroutine(some_coroutine)
1433
        some_coroutine.add_args("positional_arg1", "positional_arg2")
1434
        some_coroutine.add_kwargs(keyword_argument1=3, keyword_argument=4)
1435
    """
1436

1437
    _is_coroutine_marker = _is_coroutine_marker  # Python >=3.12
1✔
1438
    _is_coroutine = _is_coroutine  # Python <3.16
1✔
1439

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

1448

1449
cdef class DelegatedCoroutine(Coroutine):
1450
    """Coroutine provider that is injected "as is".
1451

1452
    DelegatedCoroutine is a :py:class:`Coroutine`, that is injected "as is".
1453
    """
1454

1455
    __IS_DELEGATED__ = True
1✔
1456

1457

1458
cdef class AbstractCoroutine(Coroutine):
1459
    """Abstract coroutine provider.
1460

1461
    :py:class:`AbstractCoroutine` is a :py:class:`Coroutine` provider that must
1462
    be explicitly overridden before calling.
1463

1464
    Overriding of :py:class:`AbstractCoroutine` is possible only by another
1465
    :py:class:`Coroutine` provider.
1466
    """
1467

1468
    def __call__(self, *args, **kwargs):
1469
        """Return provided object.
1470

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

1477
    def override(self, provider):
1✔
1478
        """Override provider with another provider.
1479

1480
        :param provider: Overriding provider.
1481
        :type provider: :py:class:`Provider`
1482

1483
        :raise: :py:exc:`dependency_injector.errors.Error`
1484

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

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

1498

1499
cdef class CoroutineDelegate(Delegate):
1500
    """Coroutine delegate injects delegating coroutine "as is".
1501

1502
    .. py:attribute:: provides
1503

1504
        Value that have to be provided.
1505

1506
        :type: object
1507
    """
1508

1509
    def __init__(self, coroutine):
1510
        """Initializer.
1511

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

1519

1520
cdef class ConfigurationOption(Provider):
1521
    """Child configuration option provider.
1522

1523
    This provider should not be used directly. It is a part of the
1524
    :py:class:`Configuration` provider.
1525
    """
1526

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

1535
    def __deepcopy__(self, memo):
1✔
1536
        cdef ConfigurationOption copied
1537

1538
        copied = memo.get(id(self))
1✔
1539
        if copied is not None:
1✔
1540
            return copied
×
1541

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

1550
    def __enter__(self):
1✔
1551
        return self
1✔
1552

1553
    def __exit__(self, *exc_info):
1✔
1554
        pass
1✔
1555

1556
    def __str__(self):
1557
        return represent_provider(provider=self, provides=self.get_name())
1✔
1558

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

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

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

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

1586
        value = self._root.get(self._get_self_name(), self._required)
1✔
1587
        self._cache = value
1✔
1588
        return value
1✔
1589

1590
    def _get_self_name(self):
1✔
1591
        return ".".join(
1✔
1592
            segment() if is_provider(segment) else segment for segment in self._name
1✔
1593
        )
1594

1595
    def _get_root(self):
1✔
1596
        return self._root
1✔
1597

1598
    def get_name(self):
1✔
1599
        return ".".join((self._root.get_name(), self._get_self_name()))
1✔
1600

1601
    def get_name_segments(self):
1✔
1602
        return self._name
1✔
1603

1604
    def as_int(self):
1✔
1605
        return TypedConfigurationOption(int, self)
1✔
1606

1607
    def as_float(self):
1✔
1608
        return TypedConfigurationOption(float, self)
1✔
1609

1610
    def as_(self, callback, *args, **kwargs):
1✔
1611
        return TypedConfigurationOption(callback, self, *args, **kwargs)
1✔
1612

1613
    def required(self):
1✔
1614
        return self.__class__(self._name, self._root, required=True)
1✔
1615

1616
    def is_required(self):
1✔
1617
        return self._required
1✔
1618

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

1624
    def reset_last_overriding(self):
1✔
1625
        raise Error("Configuration option does not support this method")
×
1626

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

1630
    def reset_cache(self):
1✔
1631
        self._cache = UNDEFINED
1✔
1632

1633
        for provider in self._children.values():
1✔
1634
            provider.reset_cache()
1✔
1635

1636
        for provider in self.overrides:
1✔
1637
            if isinstance(provider, (Configuration, ConfigurationOption)):
1✔
1638
                provider.reset_cache()
1✔
1639

1640
    def update(self, value):
1✔
1641
        """Set configuration options.
1642

1643
        .. deprecated:: 3.11
1644

1645
            Use :py:meth:`Configuration.override` instead.
1646

1647
        :param value: Value of configuration option.
1648
        :type value: object | dict
1649

1650
        :rtype: None
1651
        """
1652
        self.override(value)
×
1653

1654
    def from_ini(self, filepath, required=UNDEFINED, envs_required=UNDEFINED):
1✔
1655
        """Load configuration from the ini file.
1656

1657
        Loaded configuration is merged recursively over existing configuration.
1658

1659
        :param filepath: Path to the configuration file.
1660
        :type filepath: str
1661

1662
        :param required: When required is True, raise an exception if file does not exist.
1663
        :type required: bool
1664

1665
        :param envs_required: When True, raises an error on undefined environment variable.
1666
        :type envs_required: bool
1667

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

1683
        config = {}
1✔
1684
        for section in parser.sections():
1✔
1685
            config[section] = dict(parser.items(section))
1✔
1686

1687
        current_config = self.__call__()
1✔
1688
        if not current_config:
1✔
1689
            current_config = {}
1✔
1690
        self.override(merge_dicts(current_config, config))
1✔
1691

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

1695
        Loaded configuration is merged recursively over existing configuration.
1696

1697
        :param filepath: Path to the configuration file.
1698
        :type filepath: str
1699

1700
        :param required: When required is True, raise an exception if file does not exist.
1701
        :type required: bool
1702

1703
        :param loader: YAML loader, :py:class:`YamlLoader` is used if not specified.
1704
        :type loader: ``yaml.Loader``
1705

1706
        :param envs_required: When True, raises an error on undefined environment variable.
1707
        :type envs_required: bool
1708

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

1718
        if loader is None:
1✔
1719
            loader = YamlLoader
1✔
1720

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

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

1739
        current_config = self.__call__()
1✔
1740
        if not current_config:
1✔
1741
            current_config = {}
1✔
1742
        self.override(merge_dicts(current_config, config))
1✔
1743

1744
    def from_json(self, filepath, required=UNDEFINED, envs_required=UNDEFINED):
1✔
1745
        """Load configuration from a json file.
1746

1747
        Loaded configuration is merged recursively over the existing configuration.
1748

1749
        :param filepath: Path to a configuration file.
1750
        :type filepath: str
1751

1752
        :param required: When required is True, raise an exception if file does not exist.
1753
        :type required: bool
1754

1755
        :param envs_required: When True, raises an exception on undefined environment variable.
1756
        :type envs_required: bool
1757

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

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

1778
        current_config = self.__call__()
1✔
1779
        if not current_config:
1✔
1780
            current_config = {}
1✔
1781
        self.override(merge_dicts(current_config, config))
1✔
1782

1783
    def from_pydantic(self, settings, required=UNDEFINED, **kwargs):
1✔
1784
        """Load configuration from pydantic settings.
1785

1786
        Loaded configuration is merged recursively over existing configuration.
1787

1788
        :param settings: Pydantic settings instances.
1789
        :type settings: :py:class:`pydantic.BaseSettings` (pydantic v1) or
1790
            :py:class:`pydantic_settings.BaseSettings` (pydantic v2 and onwards)
1791

1792
        :param required: When required is True, raise an exception if settings dict is empty.
1793
        :type required: bool
1794

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

1799
        :rtype: None
1800
        """
1801

1802
        self.from_dict(pydantic_settings_to_dict(settings, kwargs), required=required)
1✔
1803

1804
    def from_dict(self, options, required=UNDEFINED):
1✔
1805
        """Load configuration from the dictionary.
1806

1807
        Loaded configuration is merged recursively over existing configuration.
1808

1809
        :param options: Configuration options.
1810
        :type options: dict
1811

1812
        :param required: When required is True, raise an exception if dictionary is empty.
1813
        :type required: bool
1814

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

1822
        try:
1✔
1823
            current_config = self.__call__()
1✔
1824
        except Error:
1✔
1825
            current_config = {}
1✔
1826
        else:
1827
            if not current_config:
1✔
1828
                current_config = {}
1✔
1829

1830
        self.override(merge_dicts(current_config, options))
1✔
1831

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

1835
        :param name: Name of the environment variable.
1836
        :type name: str
1837

1838
        :param default: Default value that is used if environment variable does not exist.
1839
        :type default: object
1840

1841
        :param required: When required is True, raise an exception if environment variable is undefined.
1842
        :type required: bool
1843

1844
        :param as_: Callable used for type casting (int, float, etc).
1845
        :type as_: object
1846

1847
        :rtype: None
1848
        """
1849
        value = os.environ.get(name, default)
1✔
1850

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

1857
        if as_ is not UNDEFINED:
1✔
1858
            value = as_(value)
1✔
1859

1860
        self.override(value)
1✔
1861

1862
    def from_value(self, value):
1✔
1863
        """Load configuration value.
1864

1865
        :param value: Configuration value
1866
        :type value: object
1867

1868
        :rtype: None
1869
        """
1870
        self.override(value)
1✔
1871

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

1879
    def _is_strict_mode_enabled(self):
1✔
1880
        return self._root.__strict
1✔
1881

1882

1883
cdef class TypedConfigurationOption(Callable):
1884

1885
    @property
1886
    def option(self):
1887
        return self.args[0]
1✔
1888

1889

1890
cdef class Configuration(Object):
1891
    """Configuration provider provides configuration options to the other providers.
1892

1893
    .. code-block:: python
1894

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

1910
    DEFAULT_NAME = "config"
1✔
1911

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

1921
        super().__init__(provides={})
1✔
1922
        self.set_default(default)
1✔
1923

1924
        if ini_files is None:
1✔
1925
            ini_files = []
1✔
1926
        self.set_ini_files(ini_files)
1✔
1927

1928
        if yaml_files is None:
1✔
1929
            yaml_files = []
1✔
1930
        self.set_yaml_files(yaml_files)
1✔
1931

1932
        if json_files is None:
1✔
1933
            json_files = []
1✔
1934
        self.set_json_files(json_files)
1✔
1935

1936
        if pydantic_settings is None:
1✔
1937
            pydantic_settings = []
1✔
1938
        self.set_pydantic_settings(pydantic_settings)
1✔
1939

1940
    def __deepcopy__(self, memo):
1✔
1941
        copied = memo.get(id(self))
1✔
1942
        if copied is not None:
1✔
1943
            return copied
×
1944

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

1955
        self._copy_overridings(copied, memo)
1✔
1956
        return copied
1✔
1957

1958
    def __enter__(self):
1✔
1959
        return self
1✔
1960

1961
    def __exit__(self, *exc_info):
1✔
1962
        pass
1✔
1963

1964
    def __str__(self):
1965
        return represent_provider(provider=self, provides=self._name)
1✔
1966

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

1974
        child = self._children.get(item)
1✔
1975
        if child is None:
1✔
1976
            child = ConfigurationOption((item,), self)
1✔
1977
            self._children[item] = child
1✔
1978
        return child
1✔
1979

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

1987
    def get_name(self):
1✔
1988
        """Return name."""
1989
        return self._name
1✔
1990

1991
    def set_name(self, name):
1✔
1992
        """Set name."""
1993
        self._name = name
1✔
1994
        return self
1✔
1995

1996
    def get_default(self):
1✔
1997
        """Return default."""
1998
        return self.provides
1✔
1999

2000
    def set_default(self, default):
1✔
2001
        """Set default."""
2002
        if not default:
1✔
2003
            return self
1✔
2004

2005
        assert isinstance(default, dict), default
1✔
2006
        self.set_provides(default.copy())
1✔
2007
        return self
1✔
2008

2009
    def get_strict(self):
1✔
2010
        """Return strict flag."""
2011
        return self.__strict
1✔
2012

2013
    def set_strict(self, strict):
1✔
2014
        """Set strict flag."""
2015
        self.__strict = strict
1✔
2016
        return self
1✔
2017

2018
    def get_children(self):
1✔
2019
        """Return children options."""
2020
        return self._children
1✔
2021

2022
    def set_children(self, children):
1✔
2023
        """Set children options."""
2024
        self._children = children
1✔
2025
        return self
1✔
2026

2027
    def get_ini_files(self):
1✔
2028
        """Return list of INI files."""
2029
        return list(self._ini_files)
1✔
2030

2031
    def set_ini_files(self, files):
1✔
2032
        """Set list of INI files."""
2033
        self._ini_files = list(files)
1✔
2034
        return self
1✔
2035

2036
    def get_yaml_files(self):
1✔
2037
        """Return list of YAML files."""
2038
        return list(self._yaml_files)
1✔
2039

2040
    def set_yaml_files(self, files):
1✔
2041
        """Set list of YAML files."""
2042
        self._yaml_files = list(files)
1✔
2043
        return self
1✔
2044

2045
    def get_json_files(self):
1✔
2046
        """Return list of JSON files."""
2047
        return list(self._json_files)
1✔
2048

2049
    def set_json_files(self, files):
1✔
2050
        """Set list of JSON files."""
2051
        self._json_files = list(files)
1✔
2052
        return self
1✔
2053

2054
    def get_pydantic_settings(self):
1✔
2055
        """Return list of Pydantic settings."""
2056
        return list(self._pydantic_settings)
1✔
2057

2058
    def set_pydantic_settings(self, settings):
1✔
2059
        """Set list of Pydantic settings."""
2060
        self._pydantic_settings = list(settings)
1✔
2061
        return self
1✔
2062

2063
    def load(self, required=UNDEFINED, envs_required=UNDEFINED):
1✔
2064
        """Load configuration.
2065

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

2069
        .. code-block:: python
2070

2071
           config = providers.Configuration(yaml_files=[file1, file2])
2072
           config.load()
2073

2074
        :param required: When required is True, raise an exception if file does not exist.
2075
        :type required: bool
2076

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

2083
        for file in self.get_yaml_files():
1✔
2084
            self.from_yaml(file, required=required, envs_required=envs_required)
1✔
2085

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

2089
        for settings in self.get_pydantic_settings():
1✔
2090
            self.from_pydantic(settings, required=required)
1✔
2091

2092
    def get(self, selector, required=False):
1✔
2093
        """Return configuration option.
2094

2095
        :param selector: Selector string, e.g. "option1.option2"
2096
        :type selector: str
2097

2098
        :param required: Required flag, raise error if required option is missing
2099
        :type required: bool
2100

2101
        :return: Option value.
2102
        :rtype: Any
2103
        """
2104
        value = self.__call__()
1✔
2105

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

2111
        keys = selector.split(".")
1✔
2112
        while len(keys) > 0:
1✔
2113
            key = keys.pop(0)
1✔
2114
            value = value.get(key, UNDEFINED)
1✔
2115

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

2121
        return value
1✔
2122

2123
    def set(self, selector, value):
1✔
2124
        """Override configuration option.
2125

2126
        :param selector: Selector string, e.g. "option1.option2"
2127
        :type selector: str
2128

2129
        :param value: Overriding value
2130
        :type value: Any
2131

2132
        :return: Overriding context.
2133
        :rtype: :py:class:`OverridingContext`
2134
        """
2135
        original_value = current_value = deepcopy(self.__call__())
1✔
2136

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

2147
        return self.override(original_value)
1✔
2148

2149
    def override(self, provider):
1✔
2150
        """Override provider with another provider.
2151

2152
        :param provider: Overriding provider.
2153
        :type provider: :py:class:`Provider`
2154

2155
        :raise: :py:exc:`dependency_injector.errors.Error`
2156

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

2164
    def reset_last_overriding(self):
1✔
2165
        """Reset last overriding provider.
2166

2167
        :raise: :py:exc:`dependency_injector.errors.Error` if provider is not
2168
                overridden.
2169

2170
        :rtype: None
2171
        """
2172
        super().reset_last_overriding()
1✔
2173
        self.reset_cache()
1✔
2174

2175
    def reset_override(self):
1✔
2176
        """Reset all overriding providers.
2177

2178
        :rtype: None
2179
        """
2180
        super().reset_override()
1✔
2181
        self.reset_cache()
1✔
2182

2183
    def reset_cache(self):
1✔
2184
        """Reset children providers cache.
2185

2186
        :rtype: None
2187
        """
2188
        for provider in self._children.values():
1✔
2189
            provider.reset_cache()
1✔
2190

2191
        for provider in self.overrides:
1✔
2192
            if isinstance(provider, (Configuration, ConfigurationOption)):
1✔
2193
                provider.reset_cache()
1✔
2194

2195
    def update(self, value):
1✔
2196
        """Set configuration options.
2197

2198
        .. deprecated:: 3.11
2199

2200
            Use :py:meth:`Configuration.override` instead.
2201

2202
        :param value: Value of configuration option.
2203
        :type value: object | dict
2204

2205
        :rtype: None
2206
        """
2207
        self.override(value)
1✔
2208

2209
    def from_ini(self, filepath, required=UNDEFINED, envs_required=UNDEFINED):
1✔
2210
        """Load configuration from the ini file.
2211

2212
        Loaded configuration is merged recursively over existing configuration.
2213

2214
        :param filepath: Path to the configuration file.
2215
        :type filepath: str
2216

2217
        :param required: When required is True, raise an exception if file does not exist.
2218
        :type required: bool
2219

2220
        :param envs_required: When True, raises an error on undefined environment variable.
2221
        :type envs_required: bool
2222

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

2238
        config = {}
1✔
2239
        for section in parser.sections():
1✔
2240
            config[section] = dict(parser.items(section))
1✔
2241

2242
        current_config = self.__call__()
1✔
2243
        if not current_config:
1✔
2244
            current_config = {}
1✔
2245
        self.override(merge_dicts(current_config, config))
1✔
2246

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

2250
        Loaded configuration is merged recursively over existing configuration.
2251

2252
        :param filepath: Path to the configuration file.
2253
        :type filepath: str
2254

2255
        :param required: When required is True, raise an exception if file does not exist.
2256
        :type required: bool
2257

2258
        :param loader: YAML loader, :py:class:`YamlLoader` is used if not specified.
2259
        :type loader: ``yaml.Loader``
2260

2261
        :param envs_required: When True, raises an error on undefined environment variable.
2262
        :type envs_required: bool
2263

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

2273
        if loader is None:
1✔
2274
            loader = YamlLoader
1✔
2275

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

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

2294
        current_config = self.__call__()
1✔
2295
        if not current_config:
1✔
2296
            current_config = {}
1✔
2297
        self.override(merge_dicts(current_config, config))
1✔
2298

2299
    def from_json(self, filepath, required=UNDEFINED, envs_required=UNDEFINED):
1✔
2300
        """Load configuration from a json file.
2301

2302
        Loaded configuration is merged recursively over the existing configuration.
2303

2304
        :param filepath: Path to a configuration file.
2305
        :type filepath: str
2306

2307
        :param required: When required is True, raise an exception if file does not exist.
2308
        :type required: bool
2309

2310
        :param envs_required: When True, raises an exception on undefined environment variable.
2311
        :type envs_required: bool
2312

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

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

2333
        current_config = self.__call__()
1✔
2334
        if not current_config:
1✔
2335
            current_config = {}
1✔
2336
        self.override(merge_dicts(current_config, config))
1✔
2337

2338
    def from_pydantic(self, settings, required=UNDEFINED, **kwargs):
1✔
2339
        """Load configuration from pydantic settings.
2340

2341
        Loaded configuration is merged recursively over existing configuration.
2342

2343
        :param settings: Pydantic settings instances.
2344
        :type settings: :py:class:`pydantic.BaseSettings` (pydantic v1) or
2345
            :py:class:`pydantic_settings.BaseSettings` (pydantic v2 and onwards)
2346

2347
        :param required: When required is True, raise an exception if settings dict is empty.
2348
        :type required: bool
2349

2350
        :param kwargs: Keyword arguments forwarded to ``pydantic.BaseSettings.dict()`` call.
2351
        :type kwargs: Dict[Any, Any]
2352

2353
        :rtype: None
2354
        """
2355

2356
        self.from_dict(pydantic_settings_to_dict(settings, kwargs), required=required)
1✔
2357

2358
    def from_dict(self, options, required=UNDEFINED):
1✔
2359
        """Load configuration from the dictionary.
2360

2361
        Loaded configuration is merged recursively over existing configuration.
2362

2363
        :param options: Configuration options.
2364
        :type options: dict
2365

2366
        :param required: When required is True, raise an exception if dictionary is empty.
2367
        :type required: bool
2368

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

2376
        current_config = self.__call__()
1✔
2377
        if not current_config:
1✔
2378
            current_config = {}
1✔
2379
        self.override(merge_dicts(current_config, options))
1✔
2380

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

2384
        :param name: Name of the environment variable.
2385
        :type name: str
2386

2387
        :param default: Default value that is used if environment variable does not exist.
2388
        :type default: object
2389

2390
        :param required: When required is True, raise an exception if environment variable is undefined.
2391
        :type required: bool
2392

2393
        :param as_: Callable used for type casting (int, float, etc).
2394
        :type as_: object
2395

2396
        :rtype: None
2397
        """
2398
        value = os.environ.get(name, default)
1✔
2399

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

2406
        if as_ is not UNDEFINED:
1✔
2407
            value = as_(value)
1✔
2408

2409
        self.override(value)
1✔
2410

2411
    def from_value(self, value):
1✔
2412
        """Load configuration value.
2413

2414
        :param value: Configuration value
2415
        :type value: object
2416

2417
        :rtype: None
2418
        """
2419
        self.override(value)
1✔
2420

2421
    @property
2422
    def related(self):
2423
        """Return related providers generator."""
2424
        yield from self._children.values()
1✔
2425
        yield from super().related
1✔
2426

2427
    def _is_strict_mode_enabled(self):
1✔
2428
        return self.__strict
1✔
2429

2430

2431
cdef class Factory(Provider):
2432
    r"""Factory provider creates new instance on every call.
2433

2434
    :py:class:`Factory` supports positional & keyword argument injections,
2435
    as well as attribute injections.
2436

2437
    Positional and keyword argument injections could be defined like this:
2438

2439
    .. code-block:: python
2440

2441
        factory = Factory(SomeClass,
2442
                          "positional_arg1", "positional_arg2",
2443
                          keyword_argument1=3, keyword_argument=4)
2444

2445
        # or
2446

2447
        factory = Factory(SomeClass) \
2448
            .add_args("positional_arg1", "positional_arg2") \
2449
            .add_kwargs(keyword_argument1=3, keyword_argument=4)
2450

2451
        # or
2452

2453
        factory = Factory(SomeClass)
2454
        factory.add_args("positional_arg1", "positional_arg2")
2455
        factory.add_kwargs(keyword_argument1=3, keyword_argument=4)
2456

2457

2458
    Attribute injections are defined by using
2459
    :py:meth:`Factory.add_attributes`:
2460

2461
    .. code-block:: python
2462

2463
        factory = Factory(SomeClass) \
2464
            .add_attributes(attribute1=1, attribute2=2)
2465

2466
    Retrieving of provided instance can be performed via calling
2467
    :py:class:`Factory` object:
2468

2469
    .. code-block:: python
2470

2471
        factory = Factory(SomeClass)
2472
        some_object = factory()
2473

2474
    .. py:attribute:: provided_type
2475

2476
        If provided type is defined, provider checks that providing class is
2477
        its subclass.
2478

2479
        :type: type | None
2480
    """
2481

2482
    provided_type = None
1✔
2483

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

2491
        self._attributes = tuple()
1✔
2492
        self._attributes_len = 0
1✔
2493

2494
        super(Factory, self).__init__()
1✔
2495

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

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

2510
    def __str__(self):
2511
        """Return string representation of provider.
2512

2513
        :rtype: str
2514
        """
2515
        return represent_provider(provider=self,
1✔
2516
                                  provides=self._instantiator.provides)
1✔
2517

2518
    @property
2519
    def cls(self):
2520
        """Return provided type."""
2521
        return self.provides
1✔
2522

2523
    @property
2524
    def provides(self):
2525
        """Return provider provides."""
2526
        return self._instantiator.provides
1✔
2527

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

2543
    @property
2544
    def args(self):
2545
        """Return positional argument injections."""
2546
        return self._instantiator.args
1✔
2547

2548
    def add_args(self, *args):
1✔
2549
        """Add __init__ positional argument injections.
2550

2551
        :return: Reference ``self``
2552
        """
2553
        self._instantiator.add_args(*args)
1✔
2554
        return self
1✔
2555

2556
    def set_args(self, *args):
1✔
2557
        """Set __init__ positional argument injections.
2558

2559
        Existing __init__ positional argument injections are dropped.
2560

2561
        :return: Reference ``self``
2562
        """
2563
        self._instantiator.set_args(*args)
1✔
2564
        return self
1✔
2565

2566
    def clear_args(self):
1✔
2567
        """Drop __init__ positional argument injections.
2568

2569
        :return: Reference ``self``
2570
        """
2571
        self._instantiator.clear_args()
1✔
2572
        return self
1✔
2573

2574
    @property
2575
    def kwargs(self):
2576
        """Return keyword argument injections."""
2577
        return self._instantiator.kwargs
1✔
2578

2579
    def add_kwargs(self, **kwargs):
1✔
2580
        """Add __init__ keyword argument injections.
2581

2582
        :return: Reference ``self``
2583
        """
2584
        self._instantiator.add_kwargs(**kwargs)
1✔
2585
        return self
1✔
2586

2587
    def set_kwargs(self, **kwargs):
1✔
2588
        """Set __init__ keyword argument injections.
2589

2590
        Existing __init__ keyword argument injections are dropped.
2591

2592
        :return: Reference ``self``
2593
        """
2594
        self._instantiator.set_kwargs(**kwargs)
1✔
2595
        return self
1✔
2596

2597
    def clear_kwargs(self):
1✔
2598
        """Drop __init__ keyword argument injections.
2599

2600
        :return: Reference ``self``
2601
        """
2602
        self._instantiator.clear_kwargs()
1✔
2603
        return self
1✔
2604

2605
    @property
2606
    def attributes(self):
2607
        """Return attribute injections."""
2608
        cdef int index
2609
        cdef NamedInjection attribute
2610
        cdef dict attributes
2611

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

2618
    def add_attributes(self, **kwargs):
1✔
2619
        """Add attribute injections.
2620

2621
        :return: Reference ``self``
2622
        """
2623
        self._attributes += parse_named_injections(kwargs)
1✔
2624
        self._attributes_len = len(self._attributes)
1✔
2625
        return self
1✔
2626

2627
    def set_attributes(self, **kwargs):
1✔
2628
        """Set attribute injections.
2629

2630
        Existing attribute injections are dropped.
2631

2632
        :return: Reference ``self``
2633
        """
2634
        self._attributes = parse_named_injections(kwargs)
1✔
2635
        self._attributes_len = len(self._attributes)
1✔
2636
        return self
1✔
2637

2638
    def clear_attributes(self):
1✔
2639
        """Drop attribute injections.
2640

2641
        :return: Reference ``self``
2642
        """
2643
        self._attributes = tuple()
1✔
2644
        self._attributes_len = len(self._attributes)
1✔
2645
        return self
1✔
2646

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

2656
    cpdef object _provide(self, tuple args, dict kwargs):
1✔
2657
        """Return new instance."""
2658
        return __factory_call(self, args, kwargs)
1✔
2659

2660

2661
cdef class DelegatedFactory(Factory):
2662
    """Factory that is injected "as is".
2663

2664
    .. py:attribute:: provided_type
2665

2666
        If provided type is defined, provider checks that providing class is
2667
        its subclass.
2668

2669
        :type: type | None
2670

2671
    .. py:attribute:: cls
2672
       :noindex:
2673

2674
        Class that provides object.
2675
        Alias for :py:attr:`provides`.
2676

2677
        :type: type
2678
    """
2679

2680
    __IS_DELEGATED__ = True
1✔
2681

2682

2683
cdef class AbstractFactory(Factory):
2684
    """Abstract factory provider.
2685

2686
    :py:class:`AbstractFactory` is a :py:class:`Factory` provider that must
2687
    be explicitly overridden before calling.
2688

2689
    Overriding of :py:class:`AbstractFactory` is possible only by another
2690
    :py:class:`Factory` provider.
2691
    """
2692

2693
    def __call__(self, *args, **kwargs):
2694
        """Return provided object.
2695

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

2702
    def override(self, provider):
1✔
2703
        """Override provider with another provider.
2704

2705
        :param provider: Overriding provider.
2706
        :type provider: :py:class:`Provider`
2707

2708
        :raise: :py:exc:`dependency_injector.errors.Error`
2709

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

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

2722

2723
cdef class FactoryDelegate(Delegate):
2724
    """Factory delegate injects delegating factory "as is".
2725

2726
    .. py:attribute:: provides
2727

2728
        Value that have to be provided.
2729

2730
        :type: object
2731
    """
2732

2733
    def __init__(self, factory):
2734
        """Initializer.
2735

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

2743

2744
cdef class FactoryAggregate(Aggregate):
2745
    """Factory providers aggregate.
2746

2747
    :py:class:`FactoryAggregate` is an aggregate of :py:class:`Factory`
2748
    providers.
2749

2750
    :py:class:`FactoryAggregate` is a delegated provider, meaning that it is
2751
    injected "as is".
2752

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

2758
    @property
2759
    def factories(self):
2760
        """Return dictionary of factories, read-only.
2761

2762
        Alias for ``.providers()`` attribute.
2763
        """
2764
        return self.providers
1✔
2765

2766
    def set_factories(self, factory_dict=None, **factory_kwargs):
1✔
2767
        """Set factories.
2768

2769
        Alias for ``.set_providers()`` method.
2770
        """
2771
        return self.set_providers(factory_dict, **factory_kwargs)
1✔
2772

2773

2774
cdef class BaseSingleton(Provider):
2775
    """Base class of singleton providers."""
2776

2777
    provided_type = None
1✔
2778

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

2787
    def __str__(self):
2788
        """Return string representation of provider.
2789

2790
        :rtype: str
2791
        """
2792
        return represent_provider(provider=self,
1✔
2793
                                  provides=self._instantiator.cls)
1✔
2794

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

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

2809
    @property
2810
    def cls(self):
2811
        """Return provided type."""
2812
        return self.provides
1✔
2813

2814
    @property
2815
    def provides(self):
2816
        """Return provider provides."""
2817
        return self._instantiator.provides
1✔
2818

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

2834
    @property
2835
    def args(self):
2836
        """Return positional argument injections."""
2837
        return self._instantiator.args
1✔
2838

2839
    def add_args(self, *args):
1✔
2840
        """Add __init__ positional argument injections.
2841

2842
        :return: Reference ``self``
2843
        """
2844
        self._instantiator.add_args(*args)
1✔
2845
        return self
1✔
2846

2847
    def set_args(self, *args):
1✔
2848
        """Set __init__ positional argument injections.
2849

2850
        Existing __init__ positional argument injections are dropped.
2851

2852
        :return: Reference ``self``
2853
        """
2854
        self._instantiator.set_args(*args)
1✔
2855
        return self
1✔
2856

2857
    def clear_args(self):
1✔
2858
        """Drop __init__ positional argument injections.
2859

2860
        :return: Reference ``self``
2861
        """
2862
        self._instantiator.clear_args()
1✔
2863
        return self
1✔
2864

2865
    @property
2866
    def kwargs(self):
2867
        """Return keyword argument injections."""
2868
        return self._instantiator.kwargs
1✔
2869

2870
    def add_kwargs(self, **kwargs):
1✔
2871
        """Add __init__ keyword argument injections.
2872

2873
        :return: Reference ``self``
2874
        """
2875
        self._instantiator.add_kwargs(**kwargs)
1✔
2876
        return self
1✔
2877

2878
    def set_kwargs(self, **kwargs):
1✔
2879
        """Set __init__ keyword argument injections.
2880

2881
        Existing __init__ keyword argument injections are dropped.
2882

2883
        :return: Reference ``self``
2884
        """
2885
        self._instantiator.set_kwargs(**kwargs)
1✔
2886
        return self
1✔
2887

2888
    def clear_kwargs(self):
1✔
2889
        """Drop __init__ keyword argument injections.
2890

2891
        :return: Reference ``self``
2892
        """
2893
        self._instantiator.clear_kwargs()
1✔
2894
        return self
1✔
2895

2896
    @property
2897
    def attributes(self):
2898
        """Return attribute injections."""
2899
        return self._instantiator.attributes
1✔
2900

2901
    def add_attributes(self, **kwargs):
1✔
2902
        """Add attribute injections.
2903

2904
        :return: Reference ``self``
2905
        """
2906
        self._instantiator.add_attributes(**kwargs)
1✔
2907
        return self
1✔
2908

2909
    def set_attributes(self, **kwargs):
1✔
2910
        """Set attribute injections.
2911

2912
        Existing attribute injections are dropped.
2913

2914
        :return: Reference ``self``
2915
        """
2916
        self._instantiator.set_attributes(**kwargs)
1✔
2917
        return self
1✔
2918

2919
    def clear_attributes(self):
1✔
2920
        """Drop attribute injections.
2921

2922
        :return: Reference ``self``
2923
        """
2924
        self._instantiator.clear_attributes()
1✔
2925
        return self
1✔
2926

2927
    def reset(self):
1✔
2928
        """Reset cached instance, if any.
2929

2930
        :rtype: None
2931
        """
2932
        raise NotImplementedError()
×
2933

2934
    def full_reset(self):
1✔
2935
        """Reset cached instance in current and all underlying singletons, if any.
2936

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

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

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

2963

2964
cdef class Singleton(BaseSingleton):
2965
    """Singleton provider returns same instance on every call.
2966

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

2972
    Retrieving of provided instance can be performed via calling
2973
    :py:class:`Singleton` object:
2974

2975
    .. code-block:: python
2976

2977
        singleton = Singleton(SomeClass)
2978
        some_object = singleton()
2979

2980
    .. py:attribute:: provided_type
2981

2982
        If provided type is defined, provider checks that providing class is
2983
        its subclass.
2984

2985
        :type: type | None
2986

2987
    .. py:attribute:: cls
2988
       :noindex:
2989

2990
        Class that provides object.
2991
        Alias for :py:attr:`provides`.
2992

2993
        :type: type
2994
    """
2995

2996
    def __init__(self, provides=None, *args, **kwargs):
2997
        """Initializer.
2998

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

3005
    def reset(self):
1✔
3006
        """Reset cached instance, if any.
3007

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

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

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

3027
            self._storage = instance
1✔
3028

3029
        return self._storage
1✔
3030

3031

3032
cdef class DelegatedSingleton(Singleton):
3033
    """Delegated singleton is injected "as is".
3034

3035
    .. py:attribute:: provided_type
3036

3037
        If provided type is defined, provider checks that providing class is
3038
        its subclass.
3039

3040
        :type: type | None
3041

3042
    .. py:attribute:: cls
3043
       :noindex:
3044

3045
        Class that provides object.
3046
        Alias for :py:attr:`provides`.
3047

3048
        :type: type
3049
    """
3050

3051
    __IS_DELEGATED__ = True
1✔
3052

3053

3054
cdef class ThreadSafeSingleton(BaseSingleton):
3055
    """Thread-safe singleton provider."""
3056

3057
    storage_lock = threading.RLock()
1✔
3058
    """Storage reentrant lock.
3059

3060
    :type: :py:class:`threading.RLock`
3061
    """
3062

3063
    def __init__(self, provides=None, *args, **kwargs):
3064
        """Initializer.
3065

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

3073
    def reset(self):
1✔
3074
        """Reset cached instance, if any.
3075

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

3084
    cpdef object _provide(self, tuple args, dict kwargs):
1✔
3085
        """Return single instance."""
3086
        instance = self._storage
1✔
3087

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

3101

3102
cdef class DelegatedThreadSafeSingleton(ThreadSafeSingleton):
3103
    """Delegated thread-safe singleton is injected "as is".
3104

3105
    .. py:attribute:: provided_type
3106

3107
        If provided type is defined, provider checks that providing class is
3108
        its subclass.
3109

3110
        :type: type | None
3111

3112
    .. py:attribute:: cls
3113
       :noindex:
3114

3115
        Class that provides object.
3116
        Alias for :py:attr:`provides`.
3117

3118
        :type: type
3119
    """
3120

3121
    __IS_DELEGATED__ = True
1✔
3122

3123

3124
cdef class ThreadLocalSingleton(BaseSingleton):
3125
    """Thread-local singleton provides single objects in scope of thread.
3126

3127
    .. py:attribute:: provided_type
3128

3129
        If provided type is defined, provider checks that providing class is
3130
        its subclass.
3131

3132
        :type: type | None
3133

3134
    .. py:attribute:: cls
3135
       :noindex:
3136

3137
        Class that provides object.
3138
        Alias for :py:attr:`provides`.
3139

3140
        :type: type
3141
    """
3142

3143
    def __init__(self, provides=None, *args, **kwargs):
3144
        """Initializer.
3145

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

3152
    def reset(self):
1✔
3153
        """Reset cached instance, if any.
3154

3155
        :rtype: None
3156
        """
3157
        try:
1✔
3158
            instance = self._storage.instance
1✔
3159
        except AttributeError:
1✔
3160
            return SingletonResetContext(self)
1✔
3161

3162
        if __is_future_or_coroutine(instance):
1✔
3163
            asyncio.ensure_future(instance).cancel()
×
3164

3165
        del self._storage.instance
1✔
3166

3167
        return SingletonResetContext(self)
1✔
3168

3169
    cpdef object _provide(self, tuple args, dict kwargs):
1✔
3170
        """Return single instance."""
3171
        cdef object instance
3172

3173
        try:
1✔
3174
            instance = self._storage.instance
1✔
3175
        except AttributeError:
1✔
3176
            instance = __factory_call(self._instantiator, args, kwargs)
1✔
3177

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

3185
            self._storage.instance = instance
1✔
3186
        
3187
        return instance
1✔
3188

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

3199

3200
cdef class ContextLocalSingleton(BaseSingleton):
3201
    """Context-local singleton provides single objects in scope of a context.
3202

3203
    .. py:attribute:: provided_type
3204

3205
        If provided type is defined, provider checks that providing class is
3206
        its subclass.
3207

3208
        :type: type | None
3209

3210
    .. py:attribute:: cls
3211
       :noindex:
3212

3213
        Class that provides object.
3214
        Alias for :py:attr:`provides`.
3215

3216
        :type: type
3217
    """
3218
    _none = object()
1✔
3219

3220
    def __init__(self, provides=None, *args, **kwargs):
3221
        """Initializer.
3222

3223
        :param provides: Provided type.
3224
        :type provides: type
3225
        """
3226

3227

3228
        super(ContextLocalSingleton, self).__init__(provides, *args, **kwargs)
1✔
3229
        self._storage = ContextVar("_storage", default=self._none)
1✔
3230

3231
    def reset(self):
1✔
3232
        """Reset cached instance, if any.
3233

3234
        :rtype: None
3235
        """
3236
        instance = self._storage.get()
1✔
3237
        if instance is self._none:
1✔
3238
            return SingletonResetContext(self)
1✔
3239

3240
        if __is_future_or_coroutine(instance):
1✔
3241
            asyncio.ensure_future(instance).cancel()
×
3242

3243
        self._storage.set(self._none)
1✔
3244

3245
        return SingletonResetContext(self)
1✔
3246

3247
    cpdef object _provide(self, tuple args, dict kwargs):
1✔
3248
        """Return single instance."""
3249
        cdef object instance
3250

3251
        instance = self._storage.get()
1✔
3252

3253
        if instance is self._none:
1✔
3254
            instance = __factory_call(self._instantiator, args, kwargs)
1✔
3255

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

3263
            self._storage.set(instance)
1✔
3264

3265
        return instance
1✔
3266

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

3277

3278
cdef class DelegatedThreadLocalSingleton(ThreadLocalSingleton):
3279
    """Delegated thread-local singleton is injected "as is".
3280

3281
    .. py:attribute:: provided_type
3282

3283
        If provided type is defined, provider checks that providing class is
3284
        its subclass.
3285

3286
        :type: type | None
3287

3288
    .. py:attribute:: cls
3289
       :noindex:
3290

3291
        Class that provides object.
3292
        Alias for :py:attr:`provides`.
3293

3294
        :type: type
3295
    """
3296

3297
    __IS_DELEGATED__ = True
1✔
3298

3299

3300
cdef class AbstractSingleton(BaseSingleton):
3301
    """Abstract singleton provider.
3302

3303
    :py:class:`AbstractSingleton` is a :py:class:`Singleton` provider that must
3304
    be explicitly overridden before calling.
3305

3306
    Overriding of :py:class:`AbstractSingleton` is possible only by another
3307
    :py:class:`BaseSingleton` provider.
3308
    """
3309

3310
    def __call__(self, *args, **kwargs):
3311
        """Return provided object.
3312

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

3319
    def override(self, provider):
1✔
3320
        """Override provider with another provider.
3321

3322
        :param provider: Overriding provider.
3323
        :type provider: :py:class:`Provider`
3324

3325
        :raise: :py:exc:`dependency_injector.errors.Error`
3326

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

3335
    def reset(self):
1✔
3336
        """Reset cached instance, if any.
3337

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

3344

3345
cdef class SingletonDelegate(Delegate):
3346
    """Singleton delegate injects delegating singleton "as is".
3347

3348
    .. py:attribute:: provides
3349

3350
        Value that have to be provided.
3351

3352
        :type: object
3353
    """
3354

3355
    def __init__(self, singleton):
3356
        """Initializer.
3357

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

3366

3367
cdef class List(Provider):
3368
    """List provider provides a list of values.
3369

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

3373
    Keyword argument injections are not supported.
3374

3375
    .. code-block:: python
3376

3377
        dispatcher_factory = Factory(
3378
            Dispatcher,
3379
            modules=List(
3380
                Factory(ModuleA, dependency_a),
3381
                Factory(ModuleB, dependency_b),
3382
            ),
3383
        )
3384

3385
        dispatcher = dispatcher_factory()
3386

3387
        # is equivalent to:
3388

3389
        dispatcher = Dispatcher(
3390
            modules=[
3391
                ModuleA(dependency_a),
3392
                ModuleB(dependency_b),
3393
            ],
3394
        )
3395
    """
3396

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

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

3410
        copied = _memorized_duplicate(self, memo)
1✔
3411
        copied.set_args(*deepcopy_args(self, self.args, memo))
1✔
3412
        self._copy_overridings(copied, memo)
1✔
3413
        return copied
1✔
3414

3415
    def __str__(self):
3416
        """Return string representation of provider.
3417

3418
        :rtype: str
3419
        """
3420
        return represent_provider(provider=self, provides=list(self.args))
1✔
3421

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

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

3435
    def add_args(self, *args):
1✔
3436
        """Add positional argument injections.
3437

3438
        :return: Reference ``self``
3439
        """
3440
        self._args += parse_positional_injections(args)
1✔
3441
        self._args_len = len(self._args)
1✔
3442
        return self
1✔
3443

3444
    def set_args(self, *args):
1✔
3445
        """Set positional argument injections.
3446

3447
        Existing positional argument injections are dropped.
3448

3449
        :return: Reference ``self``
3450
        """
3451
        self._args = parse_positional_injections(args)
1✔
3452
        self._args_len = len(self._args)
1✔
3453
        return self
1✔
3454

3455
    def clear_args(self):
1✔
3456
        """Drop positional argument injections.
3457

3458
        :return: Reference ``self``
3459
        """
3460
        self._args = tuple()
1✔
3461
        self._args_len = len(self._args)
1✔
3462
        return self
1✔
3463

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

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

3474

3475
cdef class Dict(Provider):
3476
    """Dict provider provides a dictionary of values.
3477

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

3481
    Positional argument injections are not supported.
3482

3483
    .. code-block:: python
3484

3485
        dispatcher_factory = Factory(
3486
            Dispatcher,
3487
            modules=Dict(
3488
                module1=Factory(ModuleA, dependency_a),
3489
                module2=Factory(ModuleB, dependency_b),
3490
            ),
3491
        )
3492

3493
        dispatcher = dispatcher_factory()
3494

3495
        # is equivalent to:
3496

3497
        dispatcher = Dispatcher(
3498
            modules={
3499
                "module1": ModuleA(dependency_a),
3500
                "module2": ModuleB(dependency_b),
3501
            },
3502
        )
3503
    """
3504

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

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

3518
        copied = _memorized_duplicate(self, memo)
1✔
3519
        self._copy_kwargs(copied, memo)
1✔
3520
        self._copy_overridings(copied, memo)
1✔
3521
        return copied
1✔
3522

3523
    def __str__(self):
3524
        """Return string representation of provider.
3525

3526
        :rtype: str
3527
        """
3528
        return represent_provider(provider=self, provides=self.kwargs)
1✔
3529

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

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

3543
    def add_kwargs(self, dict_=None, **kwargs):
1✔
3544
        """Add keyword argument injections.
3545

3546
        :return: Reference ``self``
3547
        """
3548
        if dict_ is None:
1✔
3549
            dict_ = {}
1✔
3550

3551
        self._kwargs += parse_named_injections(dict_)
1✔
3552
        self._kwargs += parse_named_injections(kwargs)
1✔
3553
        self._kwargs_len = len(self._kwargs)
1✔
3554

3555
        return self
1✔
3556

3557
    def set_kwargs(self, dict_=None, **kwargs):
1✔
3558
        """Set keyword argument injections.
3559

3560
        Existing keyword argument injections are dropped.
3561

3562
        :return: Reference ``self``
3563
        """
3564
        if dict_ is None:
1✔
3565
            dict_ = {}
1✔
3566

3567
        self._kwargs = parse_named_injections(dict_)
1✔
3568
        self._kwargs += parse_named_injections(kwargs)
1✔
3569
        self._kwargs_len = len(self._kwargs)
1✔
3570

3571
        return self
1✔
3572

3573
    def clear_kwargs(self):
1✔
3574
        """Drop keyword argument injections.
3575

3576
        :return: Reference ``self``
3577
        """
3578
        self._kwargs = tuple()
1✔
3579
        self._kwargs_len = len(self._kwargs)
1✔
3580
        return self
1✔
3581

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

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

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

3600

3601

3602
cdef class Resource(Provider):
3603
    """Resource provider provides a component with initialization and shutdown."""
3604

3605
    def __init__(self, provides=None, *args, **kwargs):
3606
        self._provides = None
1✔
3607
        self.set_provides(provides)
1✔
3608

3609
        self._initialized = False
1✔
3610
        self._resource = None
1✔
3611
        self._shutdowner = None
1✔
3612

3613
        self._args = tuple()
1✔
3614
        self._args_len = 0
1✔
3615
        self.set_args(*args)
1✔
3616

3617
        self._kwargs = tuple()
1✔
3618
        self._kwargs_len = 0
1✔
3619
        self.set_kwargs(**kwargs)
1✔
3620

3621
        super().__init__()
1✔
3622

3623
    def __deepcopy__(self, memo):
1✔
3624
        """Create and return full copy of provider."""
3625
        copied = memo.get(id(self))
1✔
3626
        if copied is not None:
1✔
3627
            return copied
×
3628

3629
        if self._initialized:
1✔
3630
            raise Error("Can not copy initialized resource")
1✔
3631

3632
        copied = _memorized_duplicate(self, memo)
1✔
3633
        copied.set_provides(_copy_if_provider(self.provides, memo))
1✔
3634
        copied.set_args(*deepcopy_args(self, self.args, memo))
1✔
3635
        copied.set_kwargs(**deepcopy_kwargs(self, self.kwargs, memo))
1✔
3636

3637
        self._copy_overridings(copied, memo)
1✔
3638

3639
        return copied
1✔
3640

3641
    def __str__(self):
3642
        """Return string representation of provider.
3643

3644
        :rtype: str
3645
        """
3646
        return represent_provider(provider=self, provides=self.provides)
1✔
3647

3648
    @property
3649
    def provides(self):
3650
        """Return provider provides."""
3651
        return self._provides
1✔
3652

3653
    def set_provides(self, provides):
1✔
3654
        """Set provider provides."""
3655
        provides = _resolve_string_import(provides)
1✔
3656
        self._provides = provides
1✔
3657
        return self
1✔
3658

3659
    @property
3660
    def args(self):
3661
        """Return positional argument injections."""
3662
        cdef int index
3663
        cdef PositionalInjection arg
3664
        cdef list args
3665

3666
        args = list()
1✔
3667
        for index in range(self._args_len):
1✔
3668
            arg = self._args[index]
1✔
3669
            args.append(arg._value)
1✔
3670
        return tuple(args)
1✔
3671

3672
    def add_args(self, *args):
1✔
3673
        """Add positional argument injections.
3674

3675
        :return: Reference ``self``
3676
        """
3677
        self._args += parse_positional_injections(args)
1✔
3678
        self._args_len = len(self._args)
1✔
3679
        return self
1✔
3680

3681
    def set_args(self, *args):
1✔
3682
        """Set positional argument injections.
3683

3684
        Existing positional argument injections are dropped.
3685

3686
        :return: Reference ``self``
3687
        """
3688
        self._args = parse_positional_injections(args)
1✔
3689
        self._args_len = len(self._args)
1✔
3690
        return self
1✔
3691

3692
    def clear_args(self):
1✔
3693
        """Drop positional argument injections.
3694

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

3701
    @property
3702
    def kwargs(self):
3703
        """Return keyword argument injections."""
3704
        cdef int index
3705
        cdef NamedInjection kwarg
3706
        cdef dict kwargs
3707

3708
        kwargs = dict()
1✔
3709
        for index in range(self._kwargs_len):
1✔
3710
            kwarg = self._kwargs[index]
1✔
3711
            kwargs[kwarg._name] = kwarg._value
1✔
3712
        return kwargs
1✔
3713

3714
    def add_kwargs(self, **kwargs):
1✔
3715
        """Add keyword argument injections.
3716

3717
        :return: Reference ``self``
3718
        """
3719
        self._kwargs += parse_named_injections(kwargs)
1✔
3720
        self._kwargs_len = len(self._kwargs)
1✔
3721
        return self
1✔
3722

3723
    def set_kwargs(self, **kwargs):
1✔
3724
        """Set keyword argument injections.
3725

3726
        Existing keyword argument injections are dropped.
3727

3728
        :return: Reference ``self``
3729
        """
3730
        self._kwargs = parse_named_injections(kwargs)
1✔
3731
        self._kwargs_len = len(self._kwargs)
1✔
3732
        return self
1✔
3733

3734
    def clear_kwargs(self):
1✔
3735
        """Drop keyword argument injections.
3736

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

3743
    @property
3744
    def initialized(self):
3745
        """Check if resource is initialized."""
3746
        return self._initialized
1✔
3747

3748
    def init(self):
1✔
3749
        """Initialize resource."""
3750
        return self.__call__()
1✔
3751

3752
    def shutdown(self):
1✔
3753
        """Shutdown resource."""
3754
        if not self._initialized:
1✔
3755
            if self._async_mode == ASYNC_MODE_ENABLED:
1✔
3756
                result = asyncio.Future()
1✔
3757
                result.set_result(None)
1✔
3758
                return result
1✔
3759
            return
1✔
3760

3761
        if self._shutdowner:
1✔
3762
            try:
1✔
3763
                shutdown = self._shutdowner(self._resource)
1✔
3764
            except StopIteration:
1✔
3765
                pass
1✔
3766
            else:
3767
                if inspect.isawaitable(shutdown):
1✔
3768
                    return self._create_shutdown_future(shutdown)
1✔
3769

3770
        self._resource = None
1✔
3771
        self._initialized = False
1✔
3772
        self._shutdowner = None
1✔
3773

3774
        if self._async_mode == ASYNC_MODE_ENABLED:
1✔
3775
            result = asyncio.Future()
1✔
3776
            result.set_result(None)
1✔
3777
            return result
1✔
3778

3779
    @property
3780
    def related(self):
3781
        """Return related providers generator."""
3782
        yield from filter(is_provider, [self.provides])
1✔
3783
        yield from filter(is_provider, self.args)
1✔
3784
        yield from filter(is_provider, self.kwargs.values())
1✔
3785
        yield from super().related
1✔
3786

3787
    cpdef object _provide(self, tuple args, dict kwargs):
1✔
3788
        if self._initialized:
1✔
3789
            return self._resource
1✔
3790

3791
        if self._is_resource_subclass(self._provides):
1✔
3792
            initializer = self._provides()
1✔
3793
            self._resource = __call(
1✔
3794
                initializer.init,
1✔
3795
                args,
3796
                self._args,
1✔
3797
                self._args_len,
3798
                kwargs,
3799
                self._kwargs,
1✔
3800
                self._kwargs_len,
3801
                self._async_mode,
3802
            )
3803
            self._shutdowner = initializer.shutdown
1✔
3804
        elif self._is_async_resource_subclass(self._provides):
1✔
3805
            initializer = self._provides()
1✔
3806
            async_init = __call(
1✔
3807
                initializer.init,
1✔
3808
                args,
3809
                self._args,
1✔
3810
                self._args_len,
3811
                kwargs,
3812
                self._kwargs,
1✔
3813
                self._kwargs_len,
3814
                self._async_mode,
3815
            )
3816
            self._initialized = True
1✔
3817
            return self._create_init_future(async_init, initializer.shutdown)
1✔
3818
        elif inspect.isgeneratorfunction(self._provides):
1✔
3819
            initializer = __call(
1✔
3820
                self._provides,
1✔
3821
                args,
3822
                self._args,
1✔
3823
                self._args_len,
3824
                kwargs,
3825
                self._kwargs,
1✔
3826
                self._kwargs_len,
3827
                self._async_mode,
3828
            )
3829
            self._resource = next(initializer)
1✔
3830
            self._shutdowner = initializer.send
1✔
3831
        elif iscoroutinefunction(self._provides):
1✔
3832
            initializer = __call(
1✔
3833
                self._provides,
1✔
3834
                args,
3835
                self._args,
1✔
3836
                self._args_len,
3837
                kwargs,
3838
                self._kwargs,
1✔
3839
                self._kwargs_len,
3840
                self._async_mode,
3841
            )
3842
            self._initialized = True
1✔
3843
            return self._create_init_future(initializer)
1✔
3844
        elif isasyncgenfunction(self._provides):
1✔
3845
            initializer = __call(
1✔
3846
                self._provides,
1✔
3847
                args,
3848
                self._args,
1✔
3849
                self._args_len,
3850
                kwargs,
3851
                self._kwargs,
1✔
3852
                self._kwargs_len,
3853
                self._async_mode,
3854
            )
3855
            self._initialized = True
1✔
3856
            return self._create_async_gen_init_future(initializer)
1✔
3857
        elif callable(self._provides):
1✔
3858
            self._resource = __call(
1✔
3859
                self._provides,
1✔
3860
                args,
3861
                self._args,
1✔
3862
                self._args_len,
3863
                kwargs,
3864
                self._kwargs,
1✔
3865
                self._kwargs_len,
3866
                self._async_mode,
3867
            )
3868
        else:
3869
            raise Error("Unknown type of resource initializer")
1✔
3870

3871
        self._initialized = True
1✔
3872
        return self._resource
1✔
3873

3874
    def _create_init_future(self, future, shutdowner=None):
1✔
3875
        callback = self._async_init_callback
1✔
3876
        if shutdowner:
1✔
3877
            callback = functools.partial(callback, shutdowner=shutdowner)
1✔
3878

3879
        future = asyncio.ensure_future(future)
1✔
3880
        future.add_done_callback(callback)
1✔
3881
        self._resource = future
1✔
3882

3883
        return future
1✔
3884

3885
    def _create_async_gen_init_future(self, initializer):
1✔
3886
        if inspect.isasyncgen(initializer):
1✔
3887
            return self._create_init_future(initializer.__anext__(), initializer.asend)
1✔
3888

3889
        future = asyncio.Future()
1✔
3890

3891
        create_initializer = asyncio.ensure_future(initializer)
1✔
3892
        create_initializer.add_done_callback(functools.partial(self._async_create_gen_callback, future))
1✔
3893
        self._resource = future
1✔
3894

3895
        return future
1✔
3896

3897
    def _async_init_callback(self, initializer, shutdowner=None):
1✔
3898
        try:
1✔
3899
            resource = initializer.result()
1✔
3900
        except Exception:
1✔
3901
            self._initialized = False
1✔
3902
        else:
3903
            self._resource = resource
1✔
3904
            self._shutdowner = shutdowner
1✔
3905

3906
    def _async_create_gen_callback(self, future, initializer_future):
1✔
3907
        initializer = initializer_future.result()
1✔
3908
        init_future = self._create_init_future(initializer.__anext__(), initializer.asend)
1✔
3909
        init_future.add_done_callback(functools.partial(self._async_trigger_result, future))
1✔
3910

3911
    def _async_trigger_result(self, future, future_result):
1✔
3912
        future.set_result(future_result.result())
1✔
3913

3914
    def _create_shutdown_future(self, shutdown_future):
1✔
3915
        future = asyncio.Future()
1✔
3916
        shutdown_future = asyncio.ensure_future(shutdown_future)
1✔
3917
        shutdown_future.add_done_callback(functools.partial(self._async_shutdown_callback, future))
1✔
3918
        return future
1✔
3919

3920
    def _async_shutdown_callback(self, future_result, shutdowner):
1✔
3921
        try:
1✔
3922
            shutdowner.result()
1✔
3923
        except StopAsyncIteration:
1✔
3924
            pass
1✔
3925

3926
        self._resource = None
1✔
3927
        self._initialized = False
1✔
3928
        self._shutdowner = None
1✔
3929

3930
        future_result.set_result(None)
1✔
3931

3932
    @staticmethod
1✔
3933
    def _is_resource_subclass(instance):
3934
        if not isinstance(instance, type):
1✔
3935
            return
1✔
3936
        from . import resources
1✔
3937
        return issubclass(instance, resources.Resource)
1✔
3938

3939
    @staticmethod
1✔
3940
    def _is_async_resource_subclass(instance):
3941
        if not isinstance(instance, type):
1✔
3942
            return
1✔
3943
        from . import resources
1✔
3944
        return issubclass(instance, resources.AsyncResource)
1✔
3945

3946

3947
cdef class Container(Provider):
3948
    """Container provider provides an instance of declarative container.
3949

3950
    .. warning::
3951
        Provider is experimental. Its interface may change.
3952
    """
3953

3954
    def __init__(self, container_cls=None, container=None, **overriding_providers):
3955
        """Initialize provider."""
3956
        self._container_cls = container_cls
1✔
3957
        self._overriding_providers = overriding_providers
1✔
3958

3959
        if container is None and container_cls:
1✔
3960
            container = container_cls()
1✔
3961
            container.assign_parent(self)
1✔
3962
        self._container = container
1✔
3963

3964
        if self._container and self._overriding_providers:
1✔
3965
            self.apply_overridings()
×
3966

3967
        self._parent = None
1✔
3968

3969
        super(Container, self).__init__()
1✔
3970

3971
    def __deepcopy__(self, memo):
1✔
3972
        """Create and return full copy of provider."""
3973
        cdef Container copied
3974

3975
        copied = memo.get(id(self))
1✔
3976
        if copied is not None:
1✔
3977
            return copied
×
3978

3979
        copied = <Container> _memorized_duplicate(self, memo)
1✔
3980
        copied._container_cls = self._container_cls
1✔
3981
        copied._container = deepcopy(self._container, memo)
1✔
3982
        copied._overriding_providers = deepcopy(self._overriding_providers, memo)
1✔
3983
        self._copy_parent(copied, memo)
1✔
3984
        self._copy_overridings(copied, memo)
1✔
3985
        return copied
1✔
3986

3987
    def __getattr__(self, name):
3988
        """Return dependency provider."""
3989
        if name.startswith("__") and name.endswith("__"):
1✔
3990
            raise AttributeError(
1✔
3991
                "'{cls}' object has no attribute "
3992
                "'{attribute_name}'".format(cls=self.__class__.__name__, attribute_name=name))
1✔
3993
        return getattr(self._container, name)
1✔
3994

3995
    @property
3996
    def providers(self):
3997
        return self._container.providers
1✔
3998

3999
    @property
4000
    def container(self):
4001
        return self._container
1✔
4002

4003
    def override(self, provider):
1✔
4004
        """Override provider with another provider."""
4005
        if not hasattr(provider, "providers"):
1✔
4006
            raise Error("Container provider {0} can be overridden only by providers container".format(self))
1✔
4007

4008
        self._container.override_providers(**provider.providers)
1✔
4009
        return super().override(provider)
1✔
4010

4011
    def reset_last_overriding(self):
1✔
4012
        """Reset last overriding provider.
4013

4014
        :raise: :py:exc:`dependency_injector.errors.Error` if provider is not
4015
                overridden.
4016

4017
        :rtype: None
4018
        """
4019
        super().reset_last_overriding()
1✔
4020
        for provider in self._container.providers.values():
1✔
4021
            if not provider.overridden:
1✔
4022
                continue
1✔
4023
            provider.reset_last_overriding()
1✔
4024

4025
    def reset_override(self):
1✔
4026
        """Reset all overriding providers.
4027

4028
        :rtype: None
4029
        """
4030
        super().reset_override()
1✔
4031
        for provider in self._container.providers.values():
1✔
4032
            if not provider.overridden:
1✔
4033
                continue
1✔
4034
            provider.reset_override()
1✔
4035

4036
    def apply_overridings(self):
1✔
4037
        """Apply container overriding.
4038

4039
        This method should not be called directly. It is called on
4040
        declarative container initialization."""
4041
        self._container.override_providers(**self._overriding_providers)
1✔
4042

4043
    @property
4044
    def related(self):
4045
        """Return related providers generator."""
4046
        yield from self.providers.values()
1✔
4047
        yield from super().related
1✔
4048

4049
    def resolve_provider_name(self, provider):
1✔
4050
        """Try to resolve provider name."""
4051
        for provider_name, container_provider in self.providers.items():
1✔
4052
            if container_provider is provider:
1✔
4053
                return provider_name
1✔
4054
        else:
4055
            raise Error(f"Can not resolve name for provider \"{provider}\"")
1✔
4056

4057
    @property
4058
    def parent(self):
4059
        """Return parent."""
4060
        return self._parent
1✔
4061

4062
    @property
4063
    def parent_name(self):
4064
        """Return parent name."""
4065
        if not self._parent:
1✔
4066
            return None
1✔
4067

4068
        name = ""
1✔
4069
        if self._parent.parent_name:
1✔
4070
            name += f"{self._parent.parent_name}."
1✔
4071
        name += f"{self._parent.resolve_provider_name(self)}"
1✔
4072

4073
        return name
1✔
4074

4075
    def assign_parent(self, parent):
1✔
4076
        """Assign parent."""
4077
        self._parent = parent
1✔
4078

4079
    def _copy_parent(self, copied, memo):
1✔
4080
        _copy_parent(self, copied, memo)
1✔
4081

4082
    cpdef object _provide(self, tuple args, dict kwargs):
1✔
4083
        """Return single instance."""
4084
        return self._container
1✔
4085

4086

4087
cdef class Selector(Provider):
4088
    """Selector provider selects provider based on the configuration value or other callable.
4089

4090
    :py:class:`Selector` provider has a callable called ``selector`` and a dictionary of providers.
4091

4092
    The ``selector`` callable is provided as a first positional argument. It can be
4093
    :py:class:`Configuration` provider or any other callable. It has to return a string value.
4094
    That value is used as a key for selecting the provider from the dictionary of providers.
4095

4096
    The providers are provided as keyword arguments. Argument name is used as a key for
4097
    selecting the provider.
4098

4099
    .. code-block:: python
4100

4101
        config = Configuration()
4102

4103
        selector = Selector(
4104
            config.one_or_another,
4105
            one=providers.Factory(SomeClass),
4106
            another=providers.Factory(SomeOtherClass),
4107
        )
4108

4109
        config.override({"one_or_another": "one"})
4110
        instance_1 = selector()
4111
        assert isinstance(instance_1, SomeClass)
4112

4113
        config.override({"one_or_another": "another"})
4114
        instance_2 = selector()
4115
        assert isinstance(instance_2, SomeOtherClass)
4116
    """
4117

4118
    def __init__(self, selector=None, **providers):
4119
        """Initialize provider."""
4120
        self._selector = None
1✔
4121
        self.set_selector(selector)
1✔
4122

4123
        self._providers = {}
1✔
4124
        self.set_providers(**providers)
1✔
4125

4126
        super(Selector, self).__init__()
1✔
4127

4128
    def __deepcopy__(self, memo):
1✔
4129
        """Create and return full copy of provider."""
4130
        copied = memo.get(id(self))
1✔
4131
        if copied is not None:
1✔
4132
            return copied
×
4133

4134
        copied = _memorized_duplicate(self, memo)
1✔
4135
        copied.set_selector(deepcopy(self._selector, memo))
1✔
4136
        copied.set_providers(**deepcopy(self._providers, memo))
1✔
4137

4138
        self._copy_overridings(copied, memo)
1✔
4139

4140
        return copied
1✔
4141

4142
    def __getattr__(self, name):
4143
        """Return provider."""
4144
        if name.startswith("__") and name.endswith("__"):
1✔
4145
            raise AttributeError(
1✔
4146
                "'{cls}' object has no attribute "
4147
                "'{attribute_name}'".format(cls=self.__class__.__name__, attribute_name=name))
1✔
4148
        if name not in self._providers:
1✔
4149
            raise AttributeError("Selector has no \"{0}\" provider".format(name))
1✔
4150

4151
        return self._providers[name]
1✔
4152

4153
    def __str__(self):
4154
        """Return string representation of provider.
4155

4156
        :rtype: str
4157
        """
4158

4159
        return "<{provider}({selector}, {providers}) at {address}>".format(
1✔
4160
            provider=".".join(( self.__class__.__module__, self.__class__.__name__)),
1✔
4161
            selector=self._selector,
4162
            providers=", ".join((
1✔
4163
                "{0}={1}".format(name, provider)
1✔
4164
                for name, provider in self._providers.items()
1✔
4165
            )),
4166
            address=hex(id(self)),
1✔
4167
        )
4168

4169
    @property
4170
    def selector(self):
4171
        """Return selector."""
4172
        return self._selector
1✔
4173

4174
    def set_selector(self, selector):
1✔
4175
        """Set selector."""
4176
        self._selector = selector
1✔
4177
        return self
1✔
4178

4179
    @property
4180
    def providers(self):
4181
        """Return providers."""
4182
        return dict(self._providers)
1✔
4183

4184
    def set_providers(self, **providers: Provider):
1✔
4185
        """Set providers."""
4186
        self._providers = providers
1✔
4187
        return self
1✔
4188

4189
    @property
4190
    def related(self):
4191
        """Return related providers generator."""
4192
        yield from filter(is_provider, [self._selector])
1✔
4193
        yield from self.providers.values()
1✔
4194
        yield from super().related
1✔
4195

4196
    cpdef object _provide(self, tuple args, dict kwargs):
1✔
4197
        """Return single instance."""
4198
        selector_value = self._selector()
1✔
4199

4200
        if selector_value is None:
1✔
4201
            raise Error("Selector value is undefined")
1✔
4202

4203
        if selector_value not in self._providers:
1✔
4204
            raise Error("Selector has no \"{0}\" provider".format(selector_value))
1✔
4205

4206
        return self._providers[selector_value](*args, **kwargs)
1✔
4207

4208

4209
cdef class ProvidedInstance(Provider):
4210
    """Provider that helps to inject attributes and items of the injected instance.
4211

4212
    You can use it like that:
4213

4214
    .. code-block:: python
4215

4216
       service = providers.Singleton(Service)
4217

4218
       client_factory = providers.Factory(
4219
           Client,
4220
           value1=service.provided[0],
4221
           value2=service.provided.value,
4222
           value3=service.provided.values[0],
4223
           value4=service.provided.get_value.call(),
4224
       )
4225

4226
    You should not create this provider directly. Get it from the ``.provided`` attribute of the
4227
    injected provider. This attribute returns the :py:class:`ProvidedInstance` for that provider.
4228

4229
    Providers that have ``.provided`` attribute:
4230

4231
    - :py:class:`Callable` and its subclasses
4232
    - :py:class:`Factory` and its subclasses
4233
    - :py:class:`Singleton` and its subclasses
4234
    - :py:class:`Object`
4235
    - :py:class:`List`
4236
    - :py:class:`Selector`
4237
    - :py:class:`Dependency`
4238
    """
4239

4240
    def __init__(self, provides=None):
4241
        self._provides = None
1✔
4242
        self.set_provides(provides)
1✔
4243
        super().__init__()
1✔
4244

4245
    def __repr__(self):
4246
        return f"{self.__class__.__name__}(\"{self._provides}\")"
1✔
4247

4248
    def __deepcopy__(self, memo):
1✔
4249
        copied = memo.get(id(self))
1✔
4250
        if copied is not None:
1✔
4251
            return copied
×
4252

4253
        copied = _memorized_duplicate(self, memo)
1✔
4254
        copied.set_provides(_copy_if_provider(self.provides, memo))
1✔
4255
        return copied
1✔
4256

4257
    def __getattr__(self, item):
4258
        return AttributeGetter(self, item)
1✔
4259

4260
    def __getitem__(self, item):
4261
        return ItemGetter(self, item)
1✔
4262

4263
    @property
4264
    def provides(self):
4265
        """Return provider provides."""
4266
        return self._provides
1✔
4267

4268
    def set_provides(self, provides):
1✔
4269
        """Set provider provides."""
4270
        self._provides = provides
1✔
4271
        return self
1✔
4272

4273
    def call(self, *args, **kwargs):
1✔
4274
        return MethodCaller(self, *args, **kwargs)
1✔
4275

4276
    @property
4277
    def related(self):
4278
        """Return related providers generator."""
4279
        if is_provider(self.provides):
1✔
4280
            yield self.provides
1✔
4281
        yield from super().related
1✔
4282

4283
    cpdef object _provide(self, tuple args, dict kwargs):
1✔
4284
        return self._provides(*args, **kwargs)
1✔
4285

4286

4287
cdef class AttributeGetter(Provider):
4288
    """Provider that returns the attribute of the injected instance.
4289

4290
    You should not create this provider directly. See :py:class:`ProvidedInstance` instead.
4291
    """
4292

4293
    def __init__(self, provides=None, name=None):
4294
        self._provides = None
1✔
4295
        self.set_provides(provides)
1✔
4296

4297
        self._name = None
1✔
4298
        self.set_name(name)
1✔
4299
        super().__init__()
1✔
4300

4301
    def __repr__(self):
4302
        return f"{self.__class__.__name__}(\"{self.name}\")"
1✔
4303

4304
    def __deepcopy__(self, memo):
1✔
4305
        copied = memo.get(id(self))
1✔
4306
        if copied is not None:
1✔
4307
            return copied
×
4308

4309
        copied = _memorized_duplicate(self, memo)
1✔
4310
        copied.set_provides(_copy_if_provider(self.provides, memo))
1✔
4311
        copied.set_name(self.name)
1✔
4312
        return copied
1✔
4313

4314
    def __getattr__(self, item):
4315
        return AttributeGetter(self, item)
1✔
4316

4317
    def __getitem__(self, item):
4318
        return ItemGetter(self, item)
1✔
4319

4320
    @property
4321
    def provides(self):
4322
        """Return provider provides."""
4323
        return self._provides
1✔
4324

4325
    def set_provides(self, provides):
1✔
4326
        """Set provider provides."""
4327
        self._provides = provides
1✔
4328
        return self
1✔
4329

4330
    @property
4331
    def name(self):
4332
        """Return name of the attribute."""
4333
        return self._name
1✔
4334

4335
    def set_name(self, name):
1✔
4336
        """Set name of the attribute."""
4337
        self._name = name
1✔
4338
        return self
1✔
4339

4340
    def call(self, *args, **kwargs):
1✔
4341
        return MethodCaller(self, *args, **kwargs)
1✔
4342

4343
    @property
4344
    def related(self):
4345
        """Return related providers generator."""
4346
        if is_provider(self.provides):
1✔
4347
            yield self.provides
1✔
4348
        yield from super().related
1✔
4349

4350
    cpdef object _provide(self, tuple args, dict kwargs):
1✔
4351
        provided = self.provides(*args, **kwargs)
1✔
4352
        if __is_future_or_coroutine(provided):
1✔
4353
            future_result = asyncio.Future()
1✔
4354
            provided = asyncio.ensure_future(provided)
1✔
4355
            provided.add_done_callback(functools.partial(self._async_provide, future_result))
1✔
4356
            return future_result
1✔
4357
        return getattr(provided, self.name)
1✔
4358

4359
    def _async_provide(self, future_result, future):
1✔
4360
        try:
1✔
4361
            provided = future.result()
1✔
4362
            result = getattr(provided, self.name)
1✔
4363
        except Exception as exception:
1✔
4364
            future_result.set_exception(exception)
1✔
4365
        else:
4366
            future_result.set_result(result)
1✔
4367

4368

4369
cdef class ItemGetter(Provider):
4370
    """Provider that returns the item of the injected instance.
4371

4372
    You should not create this provider directly. See :py:class:`ProvidedInstance` instead.
4373
    """
4374

4375
    def __init__(self, provides=None, name=None):
4376
        self._provides = None
1✔
4377
        self.set_provides(provides)
1✔
4378

4379
        self._name = None
1✔
4380
        self.set_name(name)
1✔
4381
        super().__init__()
1✔
4382

4383
    def __repr__(self):
4384
        return f"{self.__class__.__name__}(\"{self.name}\")"
1✔
4385

4386
    def __deepcopy__(self, memo):
1✔
4387
        copied = memo.get(id(self))
1✔
4388
        if copied is not None:
1✔
4389
            return copied
×
4390

4391
        copied = _memorized_duplicate(self, memo)
1✔
4392
        copied.set_provides(_copy_if_provider(self.provides, memo))
1✔
4393
        copied.set_name(self.name)
1✔
4394
        return copied
1✔
4395

4396
    def __getattr__(self, item):
4397
        return AttributeGetter(self, item)
1✔
4398

4399
    def __getitem__(self, item):
4400
        return ItemGetter(self, item)
1✔
4401

4402
    @property
4403
    def provides(self):
4404
        """Return provider"s provides."""
4405
        return self._provides
1✔
4406

4407
    def set_provides(self, provides):
1✔
4408
        """Set provider"s provides."""
4409
        self._provides = provides
1✔
4410
        return self
1✔
4411

4412
    @property
4413
    def name(self):
4414
        """Return name of the item."""
4415
        return self._name
1✔
4416

4417
    def set_name(self, name):
1✔
4418
        """Set name of the item."""
4419
        self._name = name
1✔
4420
        return self
1✔
4421

4422
    def call(self, *args, **kwargs):
1✔
4423
        return MethodCaller(self, *args, **kwargs)
1✔
4424

4425
    @property
4426
    def related(self):
4427
        """Return related providers generator."""
4428
        if is_provider(self.provides):
1✔
4429
            yield self.provides
1✔
4430
        yield from super().related
1✔
4431

4432
    cpdef object _provide(self, tuple args, dict kwargs):
1✔
4433
        provided = self.provides(*args, **kwargs)
1✔
4434
        if __is_future_or_coroutine(provided):
1✔
4435
            future_result = asyncio.Future()
1✔
4436
            provided = asyncio.ensure_future(provided)
1✔
4437
            provided.add_done_callback(functools.partial(self._async_provide, future_result))
1✔
4438
            return future_result
1✔
4439
        return provided[self.name]
1✔
4440

4441
    def _async_provide(self, future_result, future):
1✔
4442
        try:
1✔
4443
            provided = future.result()
1✔
4444
            result = provided[self.name]
1✔
4445
        except Exception as exception:
1✔
4446
            future_result.set_exception(exception)
1✔
4447
        else:
4448
            future_result.set_result(result)
1✔
4449

4450

4451
cdef class MethodCaller(Provider):
4452
    """Provider that calls the method of the injected instance.
4453

4454
    You should not create this provider directly. See :py:class:`ProvidedInstance` instead.
4455
    """
4456

4457
    def __init__(self, provides=None, *args, **kwargs):
4458
        self._provides = None
1✔
4459
        self.set_provides(provides)
1✔
4460

4461
        self._args = tuple()
1✔
4462
        self._args_len = 0
1✔
4463
        self.set_args(*args)
1✔
4464

4465
        self._kwargs = tuple()
1✔
4466
        self._kwargs_len = 0
1✔
4467
        self.set_kwargs(**kwargs)
1✔
4468

4469
        super().__init__()
1✔
4470

4471
    def __repr__(self):
4472
        return f"{self.__class__.__name__}({self.provides})"
×
4473

4474
    def __deepcopy__(self, memo):
1✔
4475
        copied = memo.get(id(self))
1✔
4476
        if copied is not None:
1✔
4477
            return copied
×
4478

4479
        copied = _memorized_duplicate(self, memo)
1✔
4480
        copied.set_provides(_copy_if_provider(self.provides, memo))
1✔
4481
        copied.set_args(*deepcopy_args(self, self.args, memo))
1✔
4482
        copied.set_kwargs(**deepcopy_kwargs(self, self.kwargs, memo))
1✔
4483
        self._copy_overridings(copied, memo)
1✔
4484
        return copied
1✔
4485

4486
    def __getattr__(self, item):
4487
        return AttributeGetter(self, item)
1✔
4488

4489
    def __getitem__(self, item):
4490
        return ItemGetter(self, item)
1✔
4491

4492
    def call(self, *args, **kwargs):
1✔
4493
        return MethodCaller(self, *args, **kwargs)
1✔
4494

4495
    @property
4496
    def provides(self):
4497
        """Return provider provides."""
4498
        return self._provides
1✔
4499

4500
    def set_provides(self, provides):
1✔
4501
        """Set provider provides."""
4502
        self._provides = provides
1✔
4503
        return self
1✔
4504

4505
    @property
4506
    def args(self):
4507
        """Return positional argument injections."""
4508
        cdef int index
4509
        cdef PositionalInjection arg
4510
        cdef list args
4511

4512
        args = list()
1✔
4513
        for index in range(self._args_len):
1✔
4514
            arg = self._args[index]
1✔
4515
            args.append(arg._value)
1✔
4516
        return tuple(args)
1✔
4517

4518
    def set_args(self, *args):
1✔
4519
        """Set positional argument injections.
4520

4521
        Existing positional argument injections are dropped.
4522

4523
        :return: Reference ``self``
4524
        """
4525
        self._args = parse_positional_injections(args)
1✔
4526
        self._args_len = len(self._args)
1✔
4527
        return self
1✔
4528

4529
    @property
4530
    def kwargs(self):
4531
        """Return keyword argument injections."""
4532
        cdef int index
4533
        cdef NamedInjection kwarg
4534
        cdef dict kwargs
4535

4536
        kwargs = dict()
1✔
4537
        for index in range(self._kwargs_len):
1✔
4538
            kwarg = self._kwargs[index]
1✔
4539
            kwargs[kwarg._name] = kwarg._value
1✔
4540
        return kwargs
1✔
4541

4542
    def set_kwargs(self, **kwargs):
1✔
4543
        """Set keyword argument injections.
4544

4545
        Existing keyword argument injections are dropped.
4546

4547
        :return: Reference ``self``
4548
        """
4549
        self._kwargs = parse_named_injections(kwargs)
1✔
4550
        self._kwargs_len = len(self._kwargs)
1✔
4551
        return self
1✔
4552

4553
    @property
4554
    def related(self):
4555
        """Return related providers generator."""
4556
        if is_provider(self.provides):
1✔
4557
            yield self.provides
1✔
4558
        yield from filter(is_provider, self.args)
1✔
4559
        yield from filter(is_provider, self.kwargs.values())
1✔
4560
        yield from super().related
1✔
4561

4562
    cpdef object _provide(self, tuple args, dict kwargs):
1✔
4563
        call = self.provides()
1✔
4564
        if __is_future_or_coroutine(call):
1✔
4565
            future_result = asyncio.Future()
1✔
4566
            call = asyncio.ensure_future(call)
1✔
4567
            call.add_done_callback(functools.partial(self._async_provide, future_result, args, kwargs))
1✔
4568
            return future_result
1✔
4569
        return __call(
1✔
4570
            call,
4571
            args,
4572
            self._args,
1✔
4573
            self._args_len,
4574
            kwargs,
4575
            self._kwargs,
1✔
4576
            self._kwargs_len,
4577
            self._async_mode,
4578
        )
4579

4580
    def _async_provide(self, future_result, args, kwargs, future):
1✔
4581
        try:
1✔
4582
            call = future.result()
1✔
4583
            result = __call(
1✔
4584
                call,
4585
                args,
1✔
4586
                self._args,
1✔
4587
                self._args_len,
4588
                kwargs,
1✔
4589
                self._kwargs,
1✔
4590
                self._kwargs_len,
4591
                self._async_mode,
4592
            )
4593
        except Exception as exception:
1✔
4594
            future_result.set_exception(exception)
1✔
4595
        else:
4596
            future_result.set_result(result)
1✔
4597

4598

4599
cdef class Injection:
4600
    """Abstract injection class."""
4601

4602

4603
cdef class PositionalInjection(Injection):
4604
    """Positional injection class."""
4605

4606
    def __init__(self, value=None):
4607
        """Initializer."""
4608
        self._value = None
1✔
4609
        self._is_provider = 0
1✔
4610
        self._is_delegated = 0
1✔
4611
        self._call = 0
1✔
4612
        self.set(value)
1✔
4613
        super(PositionalInjection, self).__init__()
1✔
4614

4615
    def __deepcopy__(self, memo):
1✔
4616
        """Create and return full copy of provider."""
4617
        copied = memo.get(id(self))
1✔
4618
        if copied is not None:
1✔
4619
            return copied
×
4620
        copied = _memorized_duplicate(self, memo)
1✔
4621
        copied.set(_copy_if_provider(self._value, memo))
1✔
4622
        return copied
1✔
4623

4624
    def get_value(self):
1✔
4625
        """Return injection value."""
4626
        return __get_value(self)
1✔
4627

4628
    def get_original_value(self):
1✔
4629
        """Return original value."""
4630
        return self._value
1✔
4631

4632
    def set(self, value):
1✔
4633
        """Set injection."""
4634
        self._value = value
1✔
4635
        self._is_provider = <int>is_provider(value)
1✔
4636
        self._is_delegated = <int>is_delegated(value)
1✔
4637
        self._call = <int>(self._is_provider == 1 and self._is_delegated == 0)
1✔
4638

4639

4640
cdef class NamedInjection(Injection):
4641
    """Keyword injection class."""
4642

4643
    def __init__(self, name=None, value=None):
4644
        """Initializer."""
4645
        self._name = name
1✔
4646
        self.set_name(name)
1✔
4647

4648
        self._value = None
1✔
4649
        self._is_provider = 0
1✔
4650
        self._is_delegated = 0
1✔
4651
        self._call = 0
1✔
4652
        self.set(value)
1✔
4653

4654
        super(NamedInjection, self).__init__()
1✔
4655

4656
    def __deepcopy__(self, memo):
1✔
4657
        """Create and return full copy of provider."""
4658
        copied = memo.get(id(self))
1✔
4659
        if copied is not None:
1✔
4660
            return copied
×
4661
        copied = _memorized_duplicate(self, memo)
1✔
4662
        copied.set_name(self.get_name())
1✔
4663
        copied.set(_copy_if_provider(self._value, memo))
1✔
4664
        return copied
1✔
4665

4666
    def get_name(self):
1✔
4667
        """Return injection name."""
4668
        return __get_name(self)
1✔
4669

4670
    def set_name(self, name):
1✔
4671
        """Set injection name."""
4672
        self._name = name
1✔
4673

4674
    def get_value(self):
1✔
4675
        """Return injection value."""
4676
        return __get_value(self)
1✔
4677

4678
    def get_original_value(self):
1✔
4679
        """Return original value."""
4680
        return self._value
1✔
4681

4682
    def set(self, value):
1✔
4683
        """Set injection."""
4684
        self._value = value
1✔
4685
        self._is_provider = <int>is_provider(value)
1✔
4686
        self._is_delegated = <int>is_delegated(value)
1✔
4687
        self._call = <int>(self._is_provider == 1 and self._is_delegated == 0)
1✔
4688

4689

4690
@cython.boundscheck(False)
1✔
4691
@cython.wraparound(False)
4692
cpdef tuple parse_positional_injections(tuple args):
4693
    """Parse positional injections."""
4694
    cdef list injections = list()
1✔
4695
    cdef int args_len = len(args)
1✔
4696

4697
    cdef int index
4698
    cdef object arg
4699
    cdef PositionalInjection injection
4700

4701
    for index in range(args_len):
1✔
4702
        arg = args[index]
1✔
4703
        injection = PositionalInjection(arg)
1✔
4704
        injections.append(injection)
1✔
4705

4706
    return tuple(injections)
1✔
4707

4708

4709
@cython.boundscheck(False)
1✔
4710
@cython.wraparound(False)
4711
cpdef tuple parse_named_injections(dict kwargs):
4712
    """Parse named injections."""
4713
    cdef list injections = list()
1✔
4714

4715
    cdef object name
4716
    cdef object arg
4717
    cdef NamedInjection injection
4718

4719
    for name, arg in kwargs.items():
1✔
4720
        injection = NamedInjection(name, arg)
1✔
4721
        injections.append(injection)
1✔
4722

4723
    return tuple(injections)
1✔
4724

4725

4726
cdef class OverridingContext:
4727
    """Provider overriding context.
4728

4729
    :py:class:`OverridingContext` is used by :py:meth:`Provider.override` for
4730
    implementing ``with`` contexts. When :py:class:`OverridingContext` is
4731
    closed, overriding that was created in this context is dropped also.
4732

4733
    .. code-block:: python
4734

4735
        with provider.override(another_provider):
4736
            assert provider.overridden
4737
        assert not provider.overridden
4738
    """
4739

4740
    def __init__(self, Provider overridden, Provider overriding):
4741
        """Initializer.
4742

4743
        :param overridden: Overridden provider.
4744
        :type overridden: :py:class:`Provider`
4745

4746
        :param overriding: Overriding provider.
4747
        :type overriding: :py:class:`Provider`
4748
        """
4749
        self._overridden = overridden
1✔
4750
        self._overriding = overriding
1✔
4751
        super(OverridingContext, self).__init__()
1✔
4752

4753
    def __enter__(self):
1✔
4754
        """Do nothing."""
4755
        return self._overriding
1✔
4756

4757
    def __exit__(self, *_):
1✔
4758
        """Exit overriding context."""
4759
        self._overridden.reset_last_overriding()
1✔
4760

4761

4762
cdef class BaseSingletonResetContext:
4763

4764
    def __init__(self, Provider provider):
4765
        self._singleton = provider
1✔
4766
        super().__init__()
1✔
4767

4768
    def __enter__(self):
1✔
4769
        return self._singleton
1✔
4770

4771
    def __exit__(self, *_):
1✔
4772
        raise NotImplementedError()
×
4773

4774

4775
cdef class SingletonResetContext(BaseSingletonResetContext):
4776

4777
    def __exit__(self, *_):
1✔
4778
        return self._singleton.reset()
1✔
4779

4780

4781
cdef class SingletonFullResetContext(BaseSingletonResetContext):
4782

4783
    def __exit__(self, *_):
1✔
4784
        return self._singleton.full_reset()
1✔
4785

4786

4787
CHILD_PROVIDERS = (Dependency, DependenciesContainer, Container)
1✔
4788

4789

4790
cpdef bint is_provider(object instance):
1✔
4791
    """Check if instance is provider instance.
4792

4793
    :param instance: Instance to be checked.
4794
    :type instance: object
4795

4796
    :rtype: bool
4797
    """
4798
    return (not isinstance(instance, type) and
1✔
4799
            getattr(instance, "__IS_PROVIDER__", False) is True)
1✔
4800

4801

4802
cpdef object ensure_is_provider(object instance):
1✔
4803
    """Check if instance is provider instance and return it.
4804

4805
    :param instance: Instance to be checked.
4806
    :type instance: object
4807

4808
    :raise: :py:exc:`dependency_injector.errors.Error` if provided instance is
4809
            not provider.
4810

4811
    :rtype: :py:class:`dependency_injector.providers.Provider`
4812
    """
4813
    if not is_provider(instance):
1✔
4814
        raise Error("Expected provider instance, got {0}".format(str(instance)))
1✔
4815
    return instance
1✔
4816

4817

4818
cpdef bint is_delegated(object instance):
1✔
4819
    """Check if instance is delegated provider.
4820

4821
    :param instance: Instance to be checked.
4822
    :type instance: object
4823

4824
    :rtype: bool
4825
    """
4826
    return (not isinstance(instance, type) and
1✔
4827
            getattr(instance, "__IS_DELEGATED__", False) is True)
1✔
4828

4829

4830
cpdef str represent_provider(object provider, object provides):
1✔
4831
    """Return string representation of provider.
4832

4833
    :param provider: Provider object
4834
    :type provider: :py:class:`dependency_injector.providers.Provider`
4835

4836
    :param provides: Object that provider provides
4837
    :type provider: object
4838

4839
    :return: String representation of provider
4840
    :rtype: str
4841
    """
4842
    return "<{provider}({provides}) at {address}>".format(
1✔
4843
        provider=".".join((provider.__class__.__module__,
1✔
4844
                           provider.__class__.__name__)),
1✔
4845
        provides=repr(provides) if provides is not None else "",
1✔
4846
        address=hex(id(provider)))
1✔
4847

4848

4849
cpdef bint is_container_instance(object instance):
1✔
4850
    """Check if instance is container instance.
4851

4852
    :param instance: Instance to be checked.
4853
    :type instance: object
4854

4855
    :rtype: bool
4856
    """
4857
    return (not isinstance(instance, type) and
1✔
4858
            getattr(instance, "__IS_CONTAINER__", False) is True)
1✔
4859

4860

4861
cpdef bint is_container_class(object instance):
1✔
4862
    """Check if instance is container class.
4863

4864
    :param instance: Instance to be checked.
4865
    :type instance: object
4866

4867
    :rtype: bool
4868
    """
4869
    return (isinstance(instance, type) and
×
4870
            getattr(instance, "__IS_CONTAINER__", False) is True)
×
4871

4872

4873
cpdef object deepcopy(object instance, dict memo=None):
1✔
4874
    """Return full copy of provider or container with providers."""
4875
    if memo is None:
1✔
4876
        memo = dict()
1✔
4877

4878
    __add_sys_streams(memo)
1✔
4879

4880
    return copy.deepcopy(instance, memo)
1✔
4881

4882

4883
cpdef tuple deepcopy_args(
1✔
4884
    Provider provider,
4885
    tuple args,
4886
    dict[int, object] memo = None,
1✔
4887
):
4888
    """A wrapper for deepcopy for positional arguments.
4889

4890
    Used to improve debugability of objects that cannot be deep-copied.
4891
    """
4892

4893
    cdef list[object] out = []
1✔
4894

4895
    for i, arg in enumerate(args):
1✔
4896
        try:
1✔
4897
            out.append(copy.deepcopy(arg, memo))
1✔
4898
        except Exception as e:
1✔
4899
            raise NonCopyableArgumentError(provider, index=i) from e
1✔
4900

4901
    return tuple(out)
1✔
4902

4903

4904
cpdef dict[str, object] deepcopy_kwargs(
1✔
4905
    Provider provider,
4906
    dict[str, object] kwargs,
4907
    dict[int, object] memo = None,
1✔
4908
):
4909
    """A wrapper for deepcopy for keyword arguments.
4910

4911
    Used to improve debugability of objects that cannot be deep-copied.
4912
    """
4913

4914
    cdef dict[str, object] out = {}
1✔
4915

4916
    for name, arg in kwargs.items():
1✔
4917
        try:
1✔
4918
            out[name] = copy.deepcopy(arg, memo)
1✔
4919
        except Exception as e:
1✔
4920
            raise NonCopyableArgumentError(provider, keyword=name) from e
1✔
4921

4922
    return out
1✔
4923

4924

4925
def __add_sys_streams(memo):
1✔
4926
    """Add system streams to memo dictionary.
4927

4928
    This helps to avoid copying of system streams while making a deepcopy of
4929
    objects graph.
4930
    """
4931
    memo[id(sys.stdin)] = sys.stdin
1✔
4932
    memo[id(sys.stdout)] = sys.stdout
1✔
4933
    memo[id(sys.stderr)] = sys.stderr
1✔
4934

4935

4936
def merge_dicts(dict1, dict2):
1✔
4937
    """Merge dictionaries recursively.
4938

4939
    :param dict1: Dictionary 1
4940
    :type dict1: dict
4941

4942
    :param dict2: Dictionary 2
4943
    :type dict2: dict
4944

4945
    :return: New resulting dictionary
4946
    :rtype: dict
4947
    """
4948
    for key, value in dict1.items():
1✔
4949
        if key in dict2:
1✔
4950
            if isinstance(value, dict) and isinstance(dict2[key], dict):
1✔
4951
                dict2[key] = merge_dicts(value, dict2[key])
1✔
4952
    result = dict1.copy()
1✔
4953
    result.update(dict2)
1✔
4954
    return result
1✔
4955

4956

4957
def traverse(*providers, types=None):
1✔
4958
    """Return providers traversal generator."""
4959
    visited = set()
1✔
4960
    to_visit = set(providers)
1✔
4961

4962
    if types:
1✔
4963
        types = tuple(types)
1✔
4964

4965
    while len(to_visit) > 0:
1✔
4966
        visiting = to_visit.pop()
1✔
4967
        visited.add(visiting)
1✔
4968

4969
        for child in visiting.related:
1✔
4970
            if child in visited:
1✔
4971
                continue
1✔
4972
            to_visit.add(child)
1✔
4973

4974
        if types and not isinstance(visiting, types):
1✔
4975
            continue
1✔
4976

4977
        yield visiting
1✔
4978

4979

4980
def isawaitable(obj):
1✔
4981
    """Check if object is a coroutine function."""
4982
    try:
×
4983
        return inspect.isawaitable(obj)
×
4984
    except AttributeError:
×
4985
        return False
×
4986

4987

4988
def iscoroutinefunction(obj):
1✔
4989
    """Check if object is a coroutine function."""
4990
    try:
1✔
4991
        return inspect.iscoroutinefunction(obj)
1✔
4992
    except AttributeError:
×
4993
        return False
×
4994

4995

4996
def isasyncgenfunction(obj):
1✔
4997
    """Check if object is an asynchronous generator function."""
4998
    try:
1✔
4999
        return inspect.isasyncgenfunction(obj)
1✔
5000
    except AttributeError:
×
5001
        return False
×
5002

5003

5004
def _resolve_string_import(provides):
1✔
5005
    if provides is None:
1✔
5006
        return provides
1✔
5007

5008
    if not isinstance(provides, str):
1✔
5009
        return provides
1✔
5010

5011
    segments = provides.split(".")
1✔
5012
    member_name = segments[-1]
1✔
5013

5014
    if len(segments) == 1:
1✔
5015
        if  member_name in dir(builtins):
1✔
5016
            module = builtins
1✔
5017
        else:
5018
            module = _resolve_calling_module()
1✔
5019
        return getattr(module, member_name)
1✔
5020

5021
    module_name = ".".join(segments[:-1])
1✔
5022

5023
    package_name = _resolve_calling_package_name()
1✔
5024
    if module_name.startswith(".") and package_name is None:
1✔
5025
        raise ImportError("Attempted relative import with no known parent package")
×
5026

5027
    module = importlib.import_module(module_name, package=package_name)
1✔
5028
    return getattr(module, member_name)
1✔
5029

5030

5031
def _resolve_calling_module():
1✔
5032
    stack = inspect.stack()
1✔
5033
    pre_last_frame = stack[0]
1✔
5034
    return inspect.getmodule(pre_last_frame[0])
1✔
5035

5036

5037
def _resolve_calling_package_name():
1✔
5038
    module = _resolve_calling_module()
1✔
5039
    return module.__package__
1✔
5040

5041

5042
cpdef _copy_parent(object from_, object to, dict memo):
1✔
5043
    """Copy and assign provider parent."""
5044
    copied_parent = (
5045
        deepcopy(from_.parent, memo)
1✔
5046
        if is_provider(from_.parent) or is_container_instance(from_.parent)
1✔
5047
        else from_.parent
1✔
5048
    )
5049
    to.assign_parent(copied_parent)
1✔
5050

5051

5052
cpdef object _memorized_duplicate(object instance, dict memo):
1✔
5053
    copied = instance.__class__()
1✔
5054
    memo[id(instance)] = copied
1✔
5055
    return copied
1✔
5056

5057

5058
cpdef object _copy_if_provider(object instance, dict memo):
1✔
5059
    if not is_provider(instance):
1✔
5060
        return instance
1✔
5061
    return deepcopy(instance, memo)
1✔
5062

5063

5064
cpdef str _class_qualname(object instance):
1✔
5065
    name = getattr(instance.__class__, "__qualname__", None)
1✔
5066
    if not name:
1✔
5067
        name = ".".join((instance.__class__.__module__, instance.__class__.__name__))
×
5068
    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