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

ets-labs / python-dependency-injector / 13615338639

02 Mar 2025 12:18PM UTC coverage: 94.537% (+0.7%) from 93.883%
13615338639

push

github

ZipFile
Remove code for EOL Python versions

14 of 15 new or added lines in 2 files covered. (93.33%)

32 existing lines in 2 files now uncovered.

3340 of 3533 relevant lines covered (94.54%)

0.95 hits per line

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

95.85
/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 contextvars
1✔
8
import copy
1✔
9
import errno
1✔
10
import functools
1✔
11
import importlib
1✔
12
import inspect
1✔
13
import json
1✔
14
import os
1✔
15
import re
1✔
16
import sys
1✔
17
import threading
1✔
18
import warnings
1✔
19
from configparser import ConfigParser as IniConfigParser
1✔
20

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

30
try:
1✔
31
    from asyncio.coroutines import _is_coroutine
1✔
32
except ImportError:
×
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:
×
48
    try:
×
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"
×
UNCOV
55
    except ImportError:
×
56
        # if it is present, ofc
57
        has_pydantic_settings = False
×
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
×
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
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
    @property
1596
    def root(self):
1597
        return self._root
1✔
1598

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1644
        .. deprecated:: 3.11
1645

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

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

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

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

1658
        Loaded configuration is merged recursively over existing configuration.
1659

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

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

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

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

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

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

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

1696
        Loaded configuration is merged recursively over existing configuration.
1697

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1787
        Loaded configuration is merged recursively over existing configuration.
1788

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

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

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

1800
        :rtype: None
1801
        """
1802

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

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

1808
        Loaded configuration is merged recursively over existing configuration.
1809

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

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

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

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

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

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

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

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

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

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

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

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

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

1861
        self.override(value)
1✔
1862

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

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

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

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

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

1883

1884
cdef class TypedConfigurationOption(Callable):
1885

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

1890

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

1894
    .. code-block:: python
1895

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

1911
    DEFAULT_NAME = "config"
1✔
1912

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2070
        .. code-block:: python
2071

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

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

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

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

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

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

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

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

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

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

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

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

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

2122
        return value
1✔
2123

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

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

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

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

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

2148
        return self.override(original_value)
1✔
2149

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

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

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

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

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

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

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

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

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

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

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

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

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

2199
        .. deprecated:: 3.11
2200

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

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

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

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

2213
        Loaded configuration is merged recursively over existing configuration.
2214

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

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

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

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

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

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

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

2251
        Loaded configuration is merged recursively over existing configuration.
2252

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2342
        Loaded configuration is merged recursively over existing configuration.
2343

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

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

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

2354
        :rtype: None
2355
        """
2356

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

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

2362
        Loaded configuration is merged recursively over existing configuration.
2363

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

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

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

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

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

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

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

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

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

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

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

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

2410
        self.override(value)
1✔
2411

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

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

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

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

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

2431

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

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

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

2440
    .. code-block:: python
2441

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

2446
        # or
2447

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

2452
        # or
2453

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

2458

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

2462
    .. code-block:: python
2463

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

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

2470
    .. code-block:: python
2471

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

2475
    .. py:attribute:: provided_type
2476

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

2480
        :type: type | None
2481
    """
2482

2483
    provided_type = None
1✔
2484

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2560
        Existing __init__ positional argument injections are dropped.
2561

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

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

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

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

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

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

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

2591
        Existing __init__ keyword argument injections are dropped.
2592

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

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

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

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

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

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

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

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

2631
        Existing attribute injections are dropped.
2632

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

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

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

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

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

2661

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

2665
    .. py:attribute:: provided_type
2666

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

2670
        :type: type | None
2671

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

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

2678
        :type: type
2679
    """
2680

2681
    __IS_DELEGATED__ = True
1✔
2682

2683

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

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

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

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

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

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

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

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

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

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

2723

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

2727
    .. py:attribute:: provides
2728

2729
        Value that have to be provided.
2730

2731
        :type: object
2732
    """
2733

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

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

2744

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

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

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

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

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

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

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

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

2774

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

2778
    provided_type = None
1✔
2779

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

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

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

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

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

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

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

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

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

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

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

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

2851
        Existing __init__ positional argument injections are dropped.
2852

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

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

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

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

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

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

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

2882
        Existing __init__ keyword argument injections are dropped.
2883

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

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

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

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

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

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

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

2913
        Existing attribute injections are dropped.
2914

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

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

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

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

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

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

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

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

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

2964

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

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

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

2976
    .. code-block:: python
2977

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

2981
    .. py:attribute:: provided_type
2982

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

2986
        :type: type | None
2987

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

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

2994
        :type: type
2995
    """
2996

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

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

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

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

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

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

3028
            self._storage = instance
1✔
3029

3030
        return self._storage
1✔
3031

3032

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

3036
    .. py:attribute:: provided_type
3037

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

3041
        :type: type | None
3042

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

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

3049
        :type: type
3050
    """
3051

3052
    __IS_DELEGATED__ = True
1✔
3053

3054

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

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

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

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

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

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

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

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

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

3102

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

3106
    .. py:attribute:: provided_type
3107

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

3111
        :type: type | None
3112

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

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

3119
        :type: type
3120
    """
3121

3122
    __IS_DELEGATED__ = True
1✔
3123

3124

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

3128
    .. py:attribute:: provided_type
3129

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

3133
        :type: type | None
3134

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

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

3141
        :type: type
3142
    """
3143

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

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

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

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

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

3166
        del self._storage.instance
1✔
3167

3168
        return SingletonResetContext(self)
1✔
3169

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

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

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

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

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

3200

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

3204
    .. py:attribute:: provided_type
3205

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

3209
        :type: type | None
3210

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

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

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

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

3224
        :param provides: Provided type.
3225
        :type provides: type
3226
        """
3227
        if not contextvars:
1✔
3228
            raise RuntimeError(
×
3229
                "Contextvars library not found. This provider "
3230
                "requires Python 3.7 or a backport of contextvars. "
3231
                "To install a backport run \"pip install contextvars\"."
3232
            )
3233

3234
        super(ContextLocalSingleton, self).__init__(provides, *args, **kwargs)
1✔
3235
        self._storage = contextvars.ContextVar("_storage", default=self._none)
1✔
3236

3237
    def reset(self):
1✔
3238
        """Reset cached instance, if any.
3239

3240
        :rtype: None
3241
        """
3242
        instance = self._storage.get()
1✔
3243
        if instance is self._none:
1✔
3244
            return SingletonResetContext(self)
1✔
3245

3246
        if __is_future_or_coroutine(instance):
1✔
3247
            asyncio.ensure_future(instance).cancel()
×
3248

3249
        self._storage.set(self._none)
1✔
3250

3251
        return SingletonResetContext(self)
1✔
3252

3253
    cpdef object _provide(self, tuple args, dict kwargs):
1✔
3254
        """Return single instance."""
3255
        cdef object instance
3256

3257
        instance = self._storage.get()
1✔
3258

3259
        if instance is self._none:
1✔
3260
            instance = __factory_call(self._instantiator, args, kwargs)
1✔
3261

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

3269
            self._storage.set(instance)
1✔
3270

3271
        return instance
1✔
3272

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

3283

3284
cdef class DelegatedThreadLocalSingleton(ThreadLocalSingleton):
3285
    """Delegated thread-local singleton is injected "as is".
3286

3287
    .. py:attribute:: provided_type
3288

3289
        If provided type is defined, provider checks that providing class is
3290
        its subclass.
3291

3292
        :type: type | None
3293

3294
    .. py:attribute:: cls
3295
       :noindex:
3296

3297
        Class that provides object.
3298
        Alias for :py:attr:`provides`.
3299

3300
        :type: type
3301
    """
3302

3303
    __IS_DELEGATED__ = True
1✔
3304

3305

3306
cdef class AbstractSingleton(BaseSingleton):
3307
    """Abstract singleton provider.
3308

3309
    :py:class:`AbstractSingleton` is a :py:class:`Singleton` provider that must
3310
    be explicitly overridden before calling.
3311

3312
    Overriding of :py:class:`AbstractSingleton` is possible only by another
3313
    :py:class:`BaseSingleton` provider.
3314
    """
3315

3316
    def __call__(self, *args, **kwargs):
3317
        """Return provided object.
3318

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

3325
    def override(self, provider):
1✔
3326
        """Override provider with another provider.
3327

3328
        :param provider: Overriding provider.
3329
        :type provider: :py:class:`Provider`
3330

3331
        :raise: :py:exc:`dependency_injector.errors.Error`
3332

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

3341
    def reset(self):
1✔
3342
        """Reset cached instance, if any.
3343

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

3350

3351
cdef class SingletonDelegate(Delegate):
3352
    """Singleton delegate injects delegating singleton "as is".
3353

3354
    .. py:attribute:: provides
3355

3356
        Value that have to be provided.
3357

3358
        :type: object
3359
    """
3360

3361
    def __init__(self, singleton):
3362
        """Initializer.
3363

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

3372

3373
cdef class List(Provider):
3374
    """List provider provides a list of values.
3375

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

3379
    Keyword argument injections are not supported.
3380

3381
    .. code-block:: python
3382

3383
        dispatcher_factory = Factory(
3384
            Dispatcher,
3385
            modules=List(
3386
                Factory(ModuleA, dependency_a),
3387
                Factory(ModuleB, dependency_b),
3388
            ),
3389
        )
3390

3391
        dispatcher = dispatcher_factory()
3392

3393
        # is equivalent to:
3394

3395
        dispatcher = Dispatcher(
3396
            modules=[
3397
                ModuleA(dependency_a),
3398
                ModuleB(dependency_b),
3399
            ],
3400
        )
3401
    """
3402

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

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

3416
        copied = _memorized_duplicate(self, memo)
1✔
3417
        copied.set_args(*deepcopy_args(self, self.args, memo))
1✔
3418
        self._copy_overridings(copied, memo)
1✔
3419
        return copied
1✔
3420

3421
    def __str__(self):
3422
        """Return string representation of provider.
3423

3424
        :rtype: str
3425
        """
3426
        return represent_provider(provider=self, provides=list(self.args))
1✔
3427

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

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

3441
    def add_args(self, *args):
1✔
3442
        """Add positional argument injections.
3443

3444
        :return: Reference ``self``
3445
        """
3446
        self._args += parse_positional_injections(args)
1✔
3447
        self._args_len = len(self._args)
1✔
3448
        return self
1✔
3449

3450
    def set_args(self, *args):
1✔
3451
        """Set positional argument injections.
3452

3453
        Existing positional argument injections are dropped.
3454

3455
        :return: Reference ``self``
3456
        """
3457
        self._args = parse_positional_injections(args)
1✔
3458
        self._args_len = len(self._args)
1✔
3459
        return self
1✔
3460

3461
    def clear_args(self):
1✔
3462
        """Drop positional argument injections.
3463

3464
        :return: Reference ``self``
3465
        """
3466
        self._args = tuple()
1✔
3467
        self._args_len = len(self._args)
1✔
3468
        return self
1✔
3469

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

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

3480

3481
cdef class Dict(Provider):
3482
    """Dict provider provides a dictionary of values.
3483

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

3487
    Positional argument injections are not supported.
3488

3489
    .. code-block:: python
3490

3491
        dispatcher_factory = Factory(
3492
            Dispatcher,
3493
            modules=Dict(
3494
                module1=Factory(ModuleA, dependency_a),
3495
                module2=Factory(ModuleB, dependency_b),
3496
            ),
3497
        )
3498

3499
        dispatcher = dispatcher_factory()
3500

3501
        # is equivalent to:
3502

3503
        dispatcher = Dispatcher(
3504
            modules={
3505
                "module1": ModuleA(dependency_a),
3506
                "module2": ModuleB(dependency_b),
3507
            },
3508
        )
3509
    """
3510

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

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

3524
        copied = _memorized_duplicate(self, memo)
1✔
3525
        self._copy_kwargs(copied, memo)
1✔
3526
        self._copy_overridings(copied, memo)
1✔
3527
        return copied
1✔
3528

3529
    def __str__(self):
3530
        """Return string representation of provider.
3531

3532
        :rtype: str
3533
        """
3534
        return represent_provider(provider=self, provides=self.kwargs)
1✔
3535

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

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

3549
    def add_kwargs(self, dict_=None, **kwargs):
1✔
3550
        """Add keyword argument injections.
3551

3552
        :return: Reference ``self``
3553
        """
3554
        if dict_ is None:
1✔
3555
            dict_ = {}
1✔
3556

3557
        self._kwargs += parse_named_injections(dict_)
1✔
3558
        self._kwargs += parse_named_injections(kwargs)
1✔
3559
        self._kwargs_len = len(self._kwargs)
1✔
3560

3561
        return self
1✔
3562

3563
    def set_kwargs(self, dict_=None, **kwargs):
1✔
3564
        """Set keyword argument injections.
3565

3566
        Existing keyword argument injections are dropped.
3567

3568
        :return: Reference ``self``
3569
        """
3570
        if dict_ is None:
1✔
3571
            dict_ = {}
1✔
3572

3573
        self._kwargs = parse_named_injections(dict_)
1✔
3574
        self._kwargs += parse_named_injections(kwargs)
1✔
3575
        self._kwargs_len = len(self._kwargs)
1✔
3576

3577
        return self
1✔
3578

3579
    def clear_kwargs(self):
1✔
3580
        """Drop keyword argument injections.
3581

3582
        :return: Reference ``self``
3583
        """
3584
        self._kwargs = tuple()
1✔
3585
        self._kwargs_len = len(self._kwargs)
1✔
3586
        return self
1✔
3587

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

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

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

3606

3607

3608
cdef class Resource(Provider):
3609
    """Resource provider provides a component with initialization and shutdown."""
3610

3611
    def __init__(self, provides=None, *args, **kwargs):
3612
        self._provides = None
1✔
3613
        self.set_provides(provides)
1✔
3614

3615
        self._initialized = False
1✔
3616
        self._resource = None
1✔
3617
        self._shutdowner = None
1✔
3618

3619
        self._args = tuple()
1✔
3620
        self._args_len = 0
1✔
3621
        self.set_args(*args)
1✔
3622

3623
        self._kwargs = tuple()
1✔
3624
        self._kwargs_len = 0
1✔
3625
        self.set_kwargs(**kwargs)
1✔
3626

3627
        super().__init__()
1✔
3628

3629
    def __deepcopy__(self, memo):
1✔
3630
        """Create and return full copy of provider."""
3631
        copied = memo.get(id(self))
1✔
3632
        if copied is not None:
1✔
3633
            return copied
×
3634

3635
        if self._initialized:
1✔
3636
            raise Error("Can not copy initialized resource")
1✔
3637

3638
        copied = _memorized_duplicate(self, memo)
1✔
3639
        copied.set_provides(_copy_if_provider(self.provides, memo))
1✔
3640
        copied.set_args(*deepcopy_args(self, self.args, memo))
1✔
3641
        copied.set_kwargs(**deepcopy_kwargs(self, self.kwargs, memo))
1✔
3642

3643
        self._copy_overridings(copied, memo)
1✔
3644

3645
        return copied
1✔
3646

3647
    def __str__(self):
3648
        """Return string representation of provider.
3649

3650
        :rtype: str
3651
        """
3652
        return represent_provider(provider=self, provides=self.provides)
1✔
3653

3654
    @property
3655
    def provides(self):
3656
        """Return provider provides."""
3657
        return self._provides
1✔
3658

3659
    def set_provides(self, provides):
1✔
3660
        """Set provider provides."""
3661
        provides = _resolve_string_import(provides)
1✔
3662
        self._provides = provides
1✔
3663
        return self
1✔
3664

3665
    @property
3666
    def args(self):
3667
        """Return positional argument injections."""
3668
        cdef int index
3669
        cdef PositionalInjection arg
3670
        cdef list args
3671

3672
        args = list()
1✔
3673
        for index in range(self._args_len):
1✔
3674
            arg = self._args[index]
1✔
3675
            args.append(arg._value)
1✔
3676
        return tuple(args)
1✔
3677

3678
    def add_args(self, *args):
1✔
3679
        """Add positional argument injections.
3680

3681
        :return: Reference ``self``
3682
        """
3683
        self._args += parse_positional_injections(args)
1✔
3684
        self._args_len = len(self._args)
1✔
3685
        return self
1✔
3686

3687
    def set_args(self, *args):
1✔
3688
        """Set positional argument injections.
3689

3690
        Existing positional argument injections are dropped.
3691

3692
        :return: Reference ``self``
3693
        """
3694
        self._args = parse_positional_injections(args)
1✔
3695
        self._args_len = len(self._args)
1✔
3696
        return self
1✔
3697

3698
    def clear_args(self):
1✔
3699
        """Drop positional argument injections.
3700

3701
        :return: Reference ``self``
3702
        """
3703
        self._args = tuple()
1✔
3704
        self._args_len = len(self._args)
1✔
3705
        return self
1✔
3706

3707
    @property
3708
    def kwargs(self):
3709
        """Return keyword argument injections."""
3710
        cdef int index
3711
        cdef NamedInjection kwarg
3712
        cdef dict kwargs
3713

3714
        kwargs = dict()
1✔
3715
        for index in range(self._kwargs_len):
1✔
3716
            kwarg = self._kwargs[index]
1✔
3717
            kwargs[kwarg._name] = kwarg._value
1✔
3718
        return kwargs
1✔
3719

3720
    def add_kwargs(self, **kwargs):
1✔
3721
        """Add keyword argument injections.
3722

3723
        :return: Reference ``self``
3724
        """
3725
        self._kwargs += parse_named_injections(kwargs)
1✔
3726
        self._kwargs_len = len(self._kwargs)
1✔
3727
        return self
1✔
3728

3729
    def set_kwargs(self, **kwargs):
1✔
3730
        """Set keyword argument injections.
3731

3732
        Existing keyword argument injections are dropped.
3733

3734
        :return: Reference ``self``
3735
        """
3736
        self._kwargs = parse_named_injections(kwargs)
1✔
3737
        self._kwargs_len = len(self._kwargs)
1✔
3738
        return self
1✔
3739

3740
    def clear_kwargs(self):
1✔
3741
        """Drop keyword argument injections.
3742

3743
        :return: Reference ``self``
3744
        """
3745
        self._kwargs = tuple()
1✔
3746
        self._kwargs_len = len(self._kwargs)
1✔
3747
        return self
1✔
3748

3749
    @property
3750
    def initialized(self):
3751
        """Check if resource is initialized."""
3752
        return self._initialized
1✔
3753

3754
    def init(self):
1✔
3755
        """Initialize resource."""
3756
        return self.__call__()
1✔
3757

3758
    def shutdown(self):
1✔
3759
        """Shutdown resource."""
3760
        if not self._initialized:
1✔
3761
            if self._async_mode == ASYNC_MODE_ENABLED:
1✔
3762
                result = asyncio.Future()
1✔
3763
                result.set_result(None)
1✔
3764
                return result
1✔
3765
            return
1✔
3766

3767
        if self._shutdowner:
1✔
3768
            try:
1✔
3769
                shutdown = self._shutdowner(self._resource)
1✔
3770
            except StopIteration:
1✔
3771
                pass
3772
            else:
3773
                if inspect.isawaitable(shutdown):
1✔
3774
                    return self._create_shutdown_future(shutdown)
1✔
3775

3776
        self._resource = None
1✔
3777
        self._initialized = False
1✔
3778
        self._shutdowner = None
1✔
3779

3780
        if self._async_mode == ASYNC_MODE_ENABLED:
1✔
3781
            result = asyncio.Future()
1✔
3782
            result.set_result(None)
1✔
3783
            return result
1✔
3784

3785
    @property
3786
    def related(self):
3787
        """Return related providers generator."""
3788
        yield from filter(is_provider, [self.provides])
1✔
3789
        yield from filter(is_provider, self.args)
1✔
3790
        yield from filter(is_provider, self.kwargs.values())
1✔
3791
        yield from super().related
1✔
3792

3793
    cpdef object _provide(self, tuple args, dict kwargs):
1✔
3794
        if self._initialized:
1✔
3795
            return self._resource
1✔
3796

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

3877
        self._initialized = True
1✔
3878
        return self._resource
1✔
3879

3880
    def _create_init_future(self, future, shutdowner=None):
1✔
3881
        callback = self._async_init_callback
1✔
3882
        if shutdowner:
1✔
3883
            callback = functools.partial(callback, shutdowner=shutdowner)
1✔
3884

3885
        future = asyncio.ensure_future(future)
1✔
3886
        future.add_done_callback(callback)
1✔
3887
        self._resource = future
1✔
3888

3889
        return future
1✔
3890

3891
    def _create_async_gen_init_future(self, initializer):
1✔
3892
        if inspect.isasyncgen(initializer):
1✔
3893
            return self._create_init_future(initializer.__anext__(), initializer.asend)
1✔
3894

3895
        future = asyncio.Future()
1✔
3896

3897
        create_initializer = asyncio.ensure_future(initializer)
1✔
3898
        create_initializer.add_done_callback(functools.partial(self._async_create_gen_callback, future))
1✔
3899
        self._resource = future
1✔
3900

3901
        return future
1✔
3902

3903
    def _async_init_callback(self, initializer, shutdowner=None):
1✔
3904
        try:
1✔
3905
            resource = initializer.result()
1✔
3906
        except Exception:
1✔
3907
            self._initialized = False
1✔
3908
        else:
3909
            self._resource = resource
1✔
3910
            self._shutdowner = shutdowner
1✔
3911

3912
    def _async_create_gen_callback(self, future, initializer_future):
1✔
3913
        initializer = initializer_future.result()
1✔
3914
        init_future = self._create_init_future(initializer.__anext__(), initializer.asend)
1✔
3915
        init_future.add_done_callback(functools.partial(self._async_trigger_result, future))
1✔
3916

3917
    def _async_trigger_result(self, future, future_result):
1✔
3918
        future.set_result(future_result.result())
1✔
3919

3920
    def _create_shutdown_future(self, shutdown_future):
1✔
3921
        future = asyncio.Future()
1✔
3922
        shutdown_future = asyncio.ensure_future(shutdown_future)
1✔
3923
        shutdown_future.add_done_callback(functools.partial(self._async_shutdown_callback, future))
1✔
3924
        return future
1✔
3925

3926
    def _async_shutdown_callback(self, future_result, shutdowner):
1✔
3927
        try:
1✔
3928
            shutdowner.result()
1✔
3929
        except StopAsyncIteration:
1✔
3930
            pass
3931

3932
        self._resource = None
1✔
3933
        self._initialized = False
1✔
3934
        self._shutdowner = None
1✔
3935

3936
        future_result.set_result(None)
1✔
3937

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

3945
    @staticmethod
1✔
3946
    def _is_async_resource_subclass(instance):
3947
        if not isinstance(instance, type):
1✔
3948
            return
1✔
3949
        from . import resources
1✔
3950
        return issubclass(instance, resources.AsyncResource)
1✔
3951

3952

3953
cdef class Container(Provider):
3954
    """Container provider provides an instance of declarative container.
3955

3956
    .. warning::
3957
        Provider is experimental. Its interface may change.
3958
    """
3959

3960
    def __init__(self, container_cls=None, container=None, **overriding_providers):
3961
        """Initialize provider."""
3962
        self._container_cls = container_cls
1✔
3963
        self._overriding_providers = overriding_providers
1✔
3964

3965
        if container is None and container_cls:
1✔
3966
            container = container_cls()
1✔
3967
            container.assign_parent(self)
1✔
3968
        self._container = container
1✔
3969

3970
        if self._container and self._overriding_providers:
1✔
3971
            self.apply_overridings()
1✔
3972

3973
        self._parent = None
1✔
3974

3975
        super(Container, self).__init__()
1✔
3976

3977
    def __deepcopy__(self, memo):
1✔
3978
        """Create and return full copy of provider."""
3979
        cdef Container copied
3980

3981
        copied = memo.get(id(self))
1✔
3982
        if copied is not None:
1✔
3983
            return copied
×
3984

3985
        copied = <Container> _memorized_duplicate(self, memo)
1✔
3986
        copied._container_cls = self._container_cls
1✔
3987
        copied._container = deepcopy(self._container, memo)
1✔
3988
        copied._overriding_providers = deepcopy(self._overriding_providers, memo)
1✔
3989
        self._copy_parent(copied, memo)
1✔
3990
        self._copy_overridings(copied, memo)
1✔
3991
        return copied
1✔
3992

3993
    def __getattr__(self, name):
3994
        """Return dependency provider."""
3995
        if name.startswith("__") and name.endswith("__"):
1✔
3996
            raise AttributeError(
1✔
3997
                "'{cls}' object has no attribute "
3998
                "'{attribute_name}'".format(cls=self.__class__.__name__, attribute_name=name))
1✔
3999
        return getattr(self._container, name)
1✔
4000

4001
    @property
4002
    def providers(self):
4003
        return self._container.providers
1✔
4004

4005
    @property
4006
    def container(self):
4007
        return self._container
1✔
4008

4009
    def override(self, provider):
1✔
4010
        """Override provider with another provider."""
4011
        if not hasattr(provider, "providers"):
1✔
4012
            raise Error("Container provider {0} can be overridden only by providers container".format(self))
1✔
4013

4014
        self._container.override_providers(**provider.providers)
1✔
4015
        return super().override(provider)
1✔
4016

4017
    def reset_last_overriding(self):
1✔
4018
        """Reset last overriding provider.
4019

4020
        :raise: :py:exc:`dependency_injector.errors.Error` if provider is not
4021
                overridden.
4022

4023
        :rtype: None
4024
        """
4025
        super().reset_last_overriding()
1✔
4026
        for provider in self._container.providers.values():
1✔
4027
            if not provider.overridden:
1✔
4028
                continue
1✔
4029
            provider.reset_last_overriding()
1✔
4030

4031
    def reset_override(self):
1✔
4032
        """Reset all overriding providers.
4033

4034
        :rtype: None
4035
        """
4036
        super().reset_override()
1✔
4037
        for provider in self._container.providers.values():
1✔
4038
            if not provider.overridden:
1✔
4039
                continue
1✔
4040
            provider.reset_override()
1✔
4041

4042
    def apply_overridings(self):
1✔
4043
        """Apply container overriding.
4044

4045
        This method should not be called directly. It is called on
4046
        declarative container initialization."""
4047
        self._container.override_providers(**self._overriding_providers)
1✔
4048

4049
    @property
4050
    def related(self):
4051
        """Return related providers generator."""
4052
        yield from self.providers.values()
1✔
4053
        yield from super().related
1✔
4054

4055
    def resolve_provider_name(self, provider):
1✔
4056
        """Try to resolve provider name."""
4057
        for provider_name, container_provider in self.providers.items():
1✔
4058
            if container_provider is provider:
1✔
4059
                return provider_name
1✔
4060
        else:
4061
            raise Error(f"Can not resolve name for provider \"{provider}\"")
1✔
4062

4063
    @property
4064
    def parent(self):
4065
        """Return parent."""
4066
        return self._parent
1✔
4067

4068
    @property
4069
    def parent_name(self):
4070
        """Return parent name."""
4071
        if not self._parent:
1✔
4072
            return None
1✔
4073

4074
        name = ""
1✔
4075
        if self._parent.parent_name:
1✔
4076
            name += f"{self._parent.parent_name}."
1✔
4077
        name += f"{self._parent.resolve_provider_name(self)}"
1✔
4078

4079
        return name
1✔
4080

4081
    def assign_parent(self, parent):
1✔
4082
        """Assign parent."""
4083
        self._parent = parent
1✔
4084

4085
    def _copy_parent(self, copied, memo):
1✔
4086
        _copy_parent(self, copied, memo)
1✔
4087

4088
    cpdef object _provide(self, tuple args, dict kwargs):
1✔
4089
        """Return single instance."""
4090
        return self._container
1✔
4091

4092

4093
cdef class Selector(Provider):
4094
    """Selector provider selects provider based on the configuration value or other callable.
4095

4096
    :py:class:`Selector` provider has a callable called ``selector`` and a dictionary of providers.
4097

4098
    The ``selector`` callable is provided as a first positional argument. It can be
4099
    :py:class:`Configuration` provider or any other callable. It has to return a string value.
4100
    That value is used as a key for selecting the provider from the dictionary of providers.
4101

4102
    The providers are provided as keyword arguments. Argument name is used as a key for
4103
    selecting the provider.
4104

4105
    .. code-block:: python
4106

4107
        config = Configuration()
4108

4109
        selector = Selector(
4110
            config.one_or_another,
4111
            one=providers.Factory(SomeClass),
4112
            another=providers.Factory(SomeOtherClass),
4113
        )
4114

4115
        config.override({"one_or_another": "one"})
4116
        instance_1 = selector()
4117
        assert isinstance(instance_1, SomeClass)
4118

4119
        config.override({"one_or_another": "another"})
4120
        instance_2 = selector()
4121
        assert isinstance(instance_2, SomeOtherClass)
4122
    """
4123

4124
    def __init__(self, selector=None, **providers):
4125
        """Initialize provider."""
4126
        self._selector = None
1✔
4127
        self.set_selector(selector)
1✔
4128

4129
        self._providers = {}
1✔
4130
        self.set_providers(**providers)
1✔
4131

4132
        super(Selector, self).__init__()
1✔
4133

4134
    def __deepcopy__(self, memo):
1✔
4135
        """Create and return full copy of provider."""
4136
        copied = memo.get(id(self))
1✔
4137
        if copied is not None:
1✔
4138
            return copied
×
4139

4140
        copied = _memorized_duplicate(self, memo)
1✔
4141
        copied.set_selector(deepcopy(self._selector, memo))
1✔
4142
        copied.set_providers(**deepcopy(self._providers, memo))
1✔
4143

4144
        self._copy_overridings(copied, memo)
1✔
4145

4146
        return copied
1✔
4147

4148
    def __getattr__(self, name):
4149
        """Return provider."""
4150
        if name.startswith("__") and name.endswith("__"):
1✔
4151
            raise AttributeError(
1✔
4152
                "'{cls}' object has no attribute "
4153
                "'{attribute_name}'".format(cls=self.__class__.__name__, attribute_name=name))
1✔
4154
        if name not in self._providers:
1✔
4155
            raise AttributeError("Selector has no \"{0}\" provider".format(name))
1✔
4156

4157
        return self._providers[name]
1✔
4158

4159
    def __str__(self):
4160
        """Return string representation of provider.
4161

4162
        :rtype: str
4163
        """
4164

4165
        return "<{provider}({selector}, {providers}) at {address}>".format(
1✔
4166
            provider=".".join(( self.__class__.__module__, self.__class__.__name__)),
1✔
4167
            selector=self._selector,
1✔
4168
            providers=", ".join((
1✔
4169
                "{0}={1}".format(name, provider)
1✔
4170
                for name, provider in self._providers.items()
1✔
4171
            )),
4172
            address=hex(id(self)),
1✔
4173
        )
4174

4175
    @property
4176
    def selector(self):
4177
        """Return selector."""
4178
        return self._selector
1✔
4179

4180
    def set_selector(self, selector):
1✔
4181
        """Set selector."""
4182
        self._selector = selector
1✔
4183
        return self
1✔
4184

4185
    @property
4186
    def providers(self):
4187
        """Return providers."""
4188
        return dict(self._providers)
1✔
4189

4190
    def set_providers(self, **providers: Provider):
1✔
4191
        """Set providers."""
4192
        self._providers = providers
1✔
4193
        return self
1✔
4194

4195
    @property
4196
    def related(self):
4197
        """Return related providers generator."""
4198
        yield from filter(is_provider, [self._selector])
1✔
4199
        yield from self.providers.values()
1✔
4200
        yield from super().related
1✔
4201

4202
    cpdef object _provide(self, tuple args, dict kwargs):
1✔
4203
        """Return single instance."""
4204
        selector_value = self._selector()
1✔
4205

4206
        if selector_value is None:
1✔
4207
            raise Error("Selector value is undefined")
1✔
4208

4209
        if selector_value not in self._providers:
1✔
4210
            raise Error("Selector has no \"{0}\" provider".format(selector_value))
1✔
4211

4212
        return self._providers[selector_value](*args, **kwargs)
1✔
4213

4214

4215
cdef class ProvidedInstance(Provider):
4216
    """Provider that helps to inject attributes and items of the injected instance.
4217

4218
    You can use it like that:
4219

4220
    .. code-block:: python
4221

4222
       service = providers.Singleton(Service)
4223

4224
       client_factory = providers.Factory(
4225
           Client,
4226
           value1=service.provided[0],
4227
           value2=service.provided.value,
4228
           value3=service.provided.values[0],
4229
           value4=service.provided.get_value.call(),
4230
       )
4231

4232
    You should not create this provider directly. Get it from the ``.provided`` attribute of the
4233
    injected provider. This attribute returns the :py:class:`ProvidedInstance` for that provider.
4234

4235
    Providers that have ``.provided`` attribute:
4236

4237
    - :py:class:`Callable` and its subclasses
4238
    - :py:class:`Factory` and its subclasses
4239
    - :py:class:`Singleton` and its subclasses
4240
    - :py:class:`Object`
4241
    - :py:class:`List`
4242
    - :py:class:`Selector`
4243
    - :py:class:`Dependency`
4244
    """
4245

4246
    def __init__(self, provides=None):
4247
        self._provides = None
1✔
4248
        self.set_provides(provides)
1✔
4249
        super().__init__()
1✔
4250

4251
    def __repr__(self):
4252
        return f"{self.__class__.__name__}(\"{self._provides}\")"
1✔
4253

4254
    def __deepcopy__(self, memo):
1✔
4255
        copied = memo.get(id(self))
1✔
4256
        if copied is not None:
1✔
4257
            return copied
×
4258

4259
        copied = _memorized_duplicate(self, memo)
1✔
4260
        copied.set_provides(_copy_if_provider(self.provides, memo))
1✔
4261
        return copied
1✔
4262

4263
    def __getattr__(self, item):
4264
        return AttributeGetter(self, item)
1✔
4265

4266
    def __getitem__(self, item):
4267
        return ItemGetter(self, item)
1✔
4268

4269
    @property
4270
    def provides(self):
4271
        """Return provider provides."""
4272
        return self._provides
1✔
4273

4274
    def set_provides(self, provides):
1✔
4275
        """Set provider provides."""
4276
        self._provides = provides
1✔
4277
        return self
1✔
4278

4279
    def call(self, *args, **kwargs):
1✔
4280
        return MethodCaller(self, *args, **kwargs)
1✔
4281

4282
    @property
4283
    def related(self):
4284
        """Return related providers generator."""
4285
        if is_provider(self.provides):
1✔
4286
            yield self.provides
1✔
4287
        yield from super().related
1✔
4288

4289
    cpdef object _provide(self, tuple args, dict kwargs):
1✔
4290
        return self._provides(*args, **kwargs)
1✔
4291

4292

4293
cdef class AttributeGetter(Provider):
4294
    """Provider that returns the attribute of the injected instance.
4295

4296
    You should not create this provider directly. See :py:class:`ProvidedInstance` instead.
4297
    """
4298

4299
    def __init__(self, provides=None, name=None):
4300
        self._provides = None
1✔
4301
        self.set_provides(provides)
1✔
4302

4303
        self._name = None
1✔
4304
        self.set_name(name)
1✔
4305
        super().__init__()
1✔
4306

4307
    def __repr__(self):
4308
        return f"{self.__class__.__name__}(\"{self.name}\")"
1✔
4309

4310
    def __deepcopy__(self, memo):
1✔
4311
        copied = memo.get(id(self))
1✔
4312
        if copied is not None:
1✔
4313
            return copied
×
4314

4315
        copied = _memorized_duplicate(self, memo)
1✔
4316
        copied.set_provides(_copy_if_provider(self.provides, memo))
1✔
4317
        copied.set_name(self.name)
1✔
4318
        return copied
1✔
4319

4320
    def __getattr__(self, item):
4321
        return AttributeGetter(self, item)
1✔
4322

4323
    def __getitem__(self, item):
4324
        return ItemGetter(self, item)
1✔
4325

4326
    @property
4327
    def provides(self):
4328
        """Return provider provides."""
4329
        return self._provides
1✔
4330

4331
    def set_provides(self, provides):
1✔
4332
        """Set provider provides."""
4333
        self._provides = provides
1✔
4334
        return self
1✔
4335

4336
    @property
4337
    def name(self):
4338
        """Return name of the attribute."""
4339
        return self._name
1✔
4340

4341
    def set_name(self, name):
1✔
4342
        """Set name of the attribute."""
4343
        self._name = name
1✔
4344
        return self
1✔
4345

4346
    def call(self, *args, **kwargs):
1✔
4347
        return MethodCaller(self, *args, **kwargs)
1✔
4348

4349
    @property
4350
    def related(self):
4351
        """Return related providers generator."""
4352
        if is_provider(self.provides):
1✔
4353
            yield self.provides
1✔
4354
        yield from super().related
1✔
4355

4356
    cpdef object _provide(self, tuple args, dict kwargs):
1✔
4357
        provided = self.provides(*args, **kwargs)
1✔
4358
        if __is_future_or_coroutine(provided):
1✔
4359
            future_result = asyncio.Future()
1✔
4360
            provided = asyncio.ensure_future(provided)
1✔
4361
            provided.add_done_callback(functools.partial(self._async_provide, future_result))
1✔
4362
            return future_result
1✔
4363
        return getattr(provided, self.name)
1✔
4364

4365
    def _async_provide(self, future_result, future):
1✔
4366
        try:
1✔
4367
            provided = future.result()
1✔
4368
            result = getattr(provided, self.name)
1✔
4369
        except Exception as exception:
1✔
4370
            future_result.set_exception(exception)
1✔
4371
        else:
4372
            future_result.set_result(result)
1✔
4373

4374

4375
cdef class ItemGetter(Provider):
4376
    """Provider that returns the item of the injected instance.
4377

4378
    You should not create this provider directly. See :py:class:`ProvidedInstance` instead.
4379
    """
4380

4381
    def __init__(self, provides=None, name=None):
4382
        self._provides = None
1✔
4383
        self.set_provides(provides)
1✔
4384

4385
        self._name = None
1✔
4386
        self.set_name(name)
1✔
4387
        super().__init__()
1✔
4388

4389
    def __repr__(self):
4390
        return f"{self.__class__.__name__}(\"{self.name}\")"
1✔
4391

4392
    def __deepcopy__(self, memo):
1✔
4393
        copied = memo.get(id(self))
1✔
4394
        if copied is not None:
1✔
4395
            return copied
×
4396

4397
        copied = _memorized_duplicate(self, memo)
1✔
4398
        copied.set_provides(_copy_if_provider(self.provides, memo))
1✔
4399
        copied.set_name(self.name)
1✔
4400
        return copied
1✔
4401

4402
    def __getattr__(self, item):
4403
        return AttributeGetter(self, item)
1✔
4404

4405
    def __getitem__(self, item):
4406
        return ItemGetter(self, item)
1✔
4407

4408
    @property
4409
    def provides(self):
4410
        """Return provider"s provides."""
4411
        return self._provides
1✔
4412

4413
    def set_provides(self, provides):
1✔
4414
        """Set provider"s provides."""
4415
        self._provides = provides
1✔
4416
        return self
1✔
4417

4418
    @property
4419
    def name(self):
4420
        """Return name of the item."""
4421
        return self._name
1✔
4422

4423
    def set_name(self, name):
1✔
4424
        """Set name of the item."""
4425
        self._name = name
1✔
4426
        return self
1✔
4427

4428
    def call(self, *args, **kwargs):
1✔
4429
        return MethodCaller(self, *args, **kwargs)
1✔
4430

4431
    @property
4432
    def related(self):
4433
        """Return related providers generator."""
4434
        if is_provider(self.provides):
1✔
4435
            yield self.provides
1✔
4436
        yield from super().related
1✔
4437

4438
    cpdef object _provide(self, tuple args, dict kwargs):
1✔
4439
        provided = self.provides(*args, **kwargs)
1✔
4440
        if __is_future_or_coroutine(provided):
1✔
4441
            future_result = asyncio.Future()
1✔
4442
            provided = asyncio.ensure_future(provided)
1✔
4443
            provided.add_done_callback(functools.partial(self._async_provide, future_result))
1✔
4444
            return future_result
1✔
4445
        return provided[self.name]
1✔
4446

4447
    def _async_provide(self, future_result, future):
1✔
4448
        try:
1✔
4449
            provided = future.result()
1✔
4450
            result = provided[self.name]
1✔
4451
        except Exception as exception:
1✔
4452
            future_result.set_exception(exception)
1✔
4453
        else:
4454
            future_result.set_result(result)
1✔
4455

4456

4457
cdef class MethodCaller(Provider):
4458
    """Provider that calls the method of the injected instance.
4459

4460
    You should not create this provider directly. See :py:class:`ProvidedInstance` instead.
4461
    """
4462

4463
    def __init__(self, provides=None, *args, **kwargs):
4464
        self._provides = None
1✔
4465
        self.set_provides(provides)
1✔
4466

4467
        self._args = tuple()
1✔
4468
        self._args_len = 0
1✔
4469
        self.set_args(*args)
1✔
4470

4471
        self._kwargs = tuple()
1✔
4472
        self._kwargs_len = 0
1✔
4473
        self.set_kwargs(**kwargs)
1✔
4474

4475
        super().__init__()
1✔
4476

4477
    def __repr__(self):
4478
        return f"{self.__class__.__name__}({self.provides})"
×
4479

4480
    def __deepcopy__(self, memo):
1✔
4481
        copied = memo.get(id(self))
1✔
4482
        if copied is not None:
1✔
4483
            return copied
×
4484

4485
        copied = _memorized_duplicate(self, memo)
1✔
4486
        copied.set_provides(_copy_if_provider(self.provides, memo))
1✔
4487
        copied.set_args(*deepcopy_args(self, self.args, memo))
1✔
4488
        copied.set_kwargs(**deepcopy_kwargs(self, self.kwargs, memo))
1✔
4489
        self._copy_overridings(copied, memo)
1✔
4490
        return copied
1✔
4491

4492
    def __getattr__(self, item):
4493
        return AttributeGetter(self, item)
1✔
4494

4495
    def __getitem__(self, item):
4496
        return ItemGetter(self, item)
1✔
4497

4498
    def call(self, *args, **kwargs):
1✔
4499
        return MethodCaller(self, *args, **kwargs)
1✔
4500

4501
    @property
4502
    def provides(self):
4503
        """Return provider provides."""
4504
        return self._provides
1✔
4505

4506
    def set_provides(self, provides):
1✔
4507
        """Set provider provides."""
4508
        self._provides = provides
1✔
4509
        return self
1✔
4510

4511
    @property
4512
    def args(self):
4513
        """Return positional argument injections."""
4514
        cdef int index
4515
        cdef PositionalInjection arg
4516
        cdef list args
4517

4518
        args = list()
1✔
4519
        for index in range(self._args_len):
1✔
4520
            arg = self._args[index]
1✔
4521
            args.append(arg._value)
1✔
4522
        return tuple(args)
1✔
4523

4524
    def set_args(self, *args):
1✔
4525
        """Set positional argument injections.
4526

4527
        Existing positional argument injections are dropped.
4528

4529
        :return: Reference ``self``
4530
        """
4531
        self._args = parse_positional_injections(args)
1✔
4532
        self._args_len = len(self._args)
1✔
4533
        return self
1✔
4534

4535
    @property
4536
    def kwargs(self):
4537
        """Return keyword argument injections."""
4538
        cdef int index
4539
        cdef NamedInjection kwarg
4540
        cdef dict kwargs
4541

4542
        kwargs = dict()
1✔
4543
        for index in range(self._kwargs_len):
1✔
4544
            kwarg = self._kwargs[index]
1✔
4545
            kwargs[kwarg._name] = kwarg._value
1✔
4546
        return kwargs
1✔
4547

4548
    def set_kwargs(self, **kwargs):
1✔
4549
        """Set keyword argument injections.
4550

4551
        Existing keyword argument injections are dropped.
4552

4553
        :return: Reference ``self``
4554
        """
4555
        self._kwargs = parse_named_injections(kwargs)
1✔
4556
        self._kwargs_len = len(self._kwargs)
1✔
4557
        return self
1✔
4558

4559
    @property
4560
    def related(self):
4561
        """Return related providers generator."""
4562
        if is_provider(self.provides):
1✔
4563
            yield self.provides
1✔
4564
        yield from filter(is_provider, self.args)
1✔
4565
        yield from filter(is_provider, self.kwargs.values())
1✔
4566
        yield from super().related
1✔
4567

4568
    cpdef object _provide(self, tuple args, dict kwargs):
1✔
4569
        call = self.provides()
1✔
4570
        if __is_future_or_coroutine(call):
1✔
4571
            future_result = asyncio.Future()
1✔
4572
            call = asyncio.ensure_future(call)
1✔
4573
            call.add_done_callback(functools.partial(self._async_provide, future_result, args, kwargs))
1✔
4574
            return future_result
1✔
4575
        return __call(
1✔
4576
            call,
4577
            args,
4578
            self._args,
1✔
4579
            self._args_len,
4580
            kwargs,
4581
            self._kwargs,
1✔
4582
            self._kwargs_len,
4583
            self._async_mode,
4584
        )
4585

4586
    def _async_provide(self, future_result, args, kwargs, future):
1✔
4587
        try:
1✔
4588
            call = future.result()
1✔
4589
            result = __call(
1✔
4590
                call,
4591
                args,
1✔
4592
                self._args,
1✔
4593
                self._args_len,
4594
                kwargs,
1✔
4595
                self._kwargs,
1✔
4596
                self._kwargs_len,
4597
                self._async_mode,
4598
            )
4599
        except Exception as exception:
1✔
4600
            future_result.set_exception(exception)
1✔
4601
        else:
4602
            future_result.set_result(result)
1✔
4603

4604

4605
cdef class Injection:
4606
    """Abstract injection class."""
4607

4608

4609
cdef class PositionalInjection(Injection):
4610
    """Positional injection class."""
4611

4612
    def __init__(self, value=None):
4613
        """Initializer."""
4614
        self._value = None
1✔
4615
        self._is_provider = 0
1✔
4616
        self._is_delegated = 0
1✔
4617
        self._call = 0
1✔
4618
        self.set(value)
1✔
4619
        super(PositionalInjection, self).__init__()
1✔
4620

4621
    def __deepcopy__(self, memo):
1✔
4622
        """Create and return full copy of provider."""
4623
        copied = memo.get(id(self))
1✔
4624
        if copied is not None:
1✔
4625
            return copied
×
4626
        copied = _memorized_duplicate(self, memo)
1✔
4627
        copied.set(_copy_if_provider(self._value, memo))
1✔
4628
        return copied
1✔
4629

4630
    def get_value(self):
1✔
4631
        """Return injection value."""
4632
        return __get_value(self)
1✔
4633

4634
    def get_original_value(self):
1✔
4635
        """Return original value."""
4636
        return self._value
1✔
4637

4638
    def set(self, value):
1✔
4639
        """Set injection."""
4640
        self._value = value
1✔
4641
        self._is_provider = <int>is_provider(value)
1✔
4642
        self._is_delegated = <int>is_delegated(value)
1✔
4643
        self._call = <int>(self._is_provider == 1 and self._is_delegated == 0)
1✔
4644

4645

4646
cdef class NamedInjection(Injection):
4647
    """Keyword injection class."""
4648

4649
    def __init__(self, name=None, value=None):
4650
        """Initializer."""
4651
        self._name = name
1✔
4652
        self.set_name(name)
1✔
4653

4654
        self._value = None
1✔
4655
        self._is_provider = 0
1✔
4656
        self._is_delegated = 0
1✔
4657
        self._call = 0
1✔
4658
        self.set(value)
1✔
4659

4660
        super(NamedInjection, self).__init__()
1✔
4661

4662
    def __deepcopy__(self, memo):
1✔
4663
        """Create and return full copy of provider."""
4664
        copied = memo.get(id(self))
1✔
4665
        if copied is not None:
1✔
4666
            return copied
×
4667
        copied = _memorized_duplicate(self, memo)
1✔
4668
        copied.set_name(self.get_name())
1✔
4669
        copied.set(_copy_if_provider(self._value, memo))
1✔
4670
        return copied
1✔
4671

4672
    def get_name(self):
1✔
4673
        """Return injection name."""
4674
        return __get_name(self)
1✔
4675

4676
    def set_name(self, name):
1✔
4677
        """Set injection name."""
4678
        self._name = name
1✔
4679

4680
    def get_value(self):
1✔
4681
        """Return injection value."""
4682
        return __get_value(self)
1✔
4683

4684
    def get_original_value(self):
1✔
4685
        """Return original value."""
4686
        return self._value
1✔
4687

4688
    def set(self, value):
1✔
4689
        """Set injection."""
4690
        self._value = value
1✔
4691
        self._is_provider = <int>is_provider(value)
1✔
4692
        self._is_delegated = <int>is_delegated(value)
1✔
4693
        self._call = <int>(self._is_provider == 1 and self._is_delegated == 0)
1✔
4694

4695

4696
@cython.boundscheck(False)
4697
@cython.wraparound(False)
4698
cpdef tuple parse_positional_injections(tuple args):
1✔
4699
    """Parse positional injections."""
4700
    cdef list injections = list()
1✔
4701
    cdef int args_len = len(args)
1✔
4702

4703
    cdef int index
4704
    cdef object arg
4705
    cdef PositionalInjection injection
4706

4707
    for index in range(args_len):
1✔
4708
        arg = args[index]
1✔
4709
        injection = PositionalInjection(arg)
1✔
4710
        injections.append(injection)
1✔
4711

4712
    return tuple(injections)
1✔
4713

4714

4715
@cython.boundscheck(False)
4716
@cython.wraparound(False)
4717
cpdef tuple parse_named_injections(dict kwargs):
1✔
4718
    """Parse named injections."""
4719
    cdef list injections = list()
1✔
4720

4721
    cdef object name
4722
    cdef object arg
4723
    cdef NamedInjection injection
4724

4725
    for name, arg in kwargs.items():
1✔
4726
        injection = NamedInjection(name, arg)
1✔
4727
        injections.append(injection)
1✔
4728

4729
    return tuple(injections)
1✔
4730

4731

4732
cdef class OverridingContext:
4733
    """Provider overriding context.
4734

4735
    :py:class:`OverridingContext` is used by :py:meth:`Provider.override` for
4736
    implementing ``with`` contexts. When :py:class:`OverridingContext` is
4737
    closed, overriding that was created in this context is dropped also.
4738

4739
    .. code-block:: python
4740

4741
        with provider.override(another_provider):
4742
            assert provider.overridden
4743
        assert not provider.overridden
4744
    """
4745

4746
    def __init__(self, Provider overridden, Provider overriding):
4747
        """Initializer.
4748

4749
        :param overridden: Overridden provider.
4750
        :type overridden: :py:class:`Provider`
4751

4752
        :param overriding: Overriding provider.
4753
        :type overriding: :py:class:`Provider`
4754
        """
4755
        self._overridden = overridden
1✔
4756
        self._overriding = overriding
1✔
4757
        super(OverridingContext, self).__init__()
1✔
4758

4759
    def __enter__(self):
1✔
4760
        """Do nothing."""
4761
        return self._overriding
1✔
4762

4763
    def __exit__(self, *_):
1✔
4764
        """Exit overriding context."""
4765
        self._overridden.reset_last_overriding()
1✔
4766

4767

4768
cdef class BaseSingletonResetContext:
4769

4770
    def __init__(self, Provider provider):
4771
        self._singleton = provider
1✔
4772
        super().__init__()
1✔
4773

4774
    def __enter__(self):
1✔
4775
        return self._singleton
1✔
4776

4777
    def __exit__(self, *_):
1✔
4778
        raise NotImplementedError()
×
4779

4780

4781
cdef class SingletonResetContext(BaseSingletonResetContext):
4782

4783
    def __exit__(self, *_):
1✔
4784
        return self._singleton.reset()
1✔
4785

4786

4787
cdef class SingletonFullResetContext(BaseSingletonResetContext):
4788

4789
    def __exit__(self, *_):
1✔
4790
        return self._singleton.full_reset()
1✔
4791

4792

4793
CHILD_PROVIDERS = (Dependency, DependenciesContainer, Container)
1✔
4794

4795

4796
cpdef bint is_provider(object instance):
1✔
4797
    """Check if instance is provider instance.
4798

4799
    :param instance: Instance to be checked.
4800
    :type instance: object
4801

4802
    :rtype: bool
4803
    """
4804
    return (not isinstance(instance, type) and
1✔
4805
            getattr(instance, "__IS_PROVIDER__", False) is True)
1✔
4806

4807

4808
cpdef object ensure_is_provider(object instance):
1✔
4809
    """Check if instance is provider instance and return it.
4810

4811
    :param instance: Instance to be checked.
4812
    :type instance: object
4813

4814
    :raise: :py:exc:`dependency_injector.errors.Error` if provided instance is
4815
            not provider.
4816

4817
    :rtype: :py:class:`dependency_injector.providers.Provider`
4818
    """
4819
    if not is_provider(instance):
1✔
4820
        raise Error("Expected provider instance, got {0}".format(str(instance)))
1✔
4821
    return instance
1✔
4822

4823

4824
cpdef bint is_delegated(object instance):
1✔
4825
    """Check if instance is delegated provider.
4826

4827
    :param instance: Instance to be checked.
4828
    :type instance: object
4829

4830
    :rtype: bool
4831
    """
4832
    return (not isinstance(instance, type) and
1✔
4833
            getattr(instance, "__IS_DELEGATED__", False) is True)
1✔
4834

4835

4836
cpdef str represent_provider(object provider, object provides):
1✔
4837
    """Return string representation of provider.
4838

4839
    :param provider: Provider object
4840
    :type provider: :py:class:`dependency_injector.providers.Provider`
4841

4842
    :param provides: Object that provider provides
4843
    :type provider: object
4844

4845
    :return: String representation of provider
4846
    :rtype: str
4847
    """
4848
    return "<{provider}({provides}) at {address}>".format(
1✔
4849
        provider=".".join((provider.__class__.__module__,
1✔
4850
                           provider.__class__.__name__)),
1✔
4851
        provides=repr(provides) if provides is not None else "",
1✔
4852
        address=hex(id(provider)))
1✔
4853

4854

4855
cpdef bint is_container_instance(object instance):
1✔
4856
    """Check if instance is container instance.
4857

4858
    :param instance: Instance to be checked.
4859
    :type instance: object
4860

4861
    :rtype: bool
4862
    """
4863
    return (not isinstance(instance, type) and
1✔
4864
            getattr(instance, "__IS_CONTAINER__", False) is True)
1✔
4865

4866

4867
cpdef bint is_container_class(object instance):
1✔
4868
    """Check if instance is container class.
4869

4870
    :param instance: Instance to be checked.
4871
    :type instance: object
4872

4873
    :rtype: bool
4874
    """
NEW
4875
    return (isinstance(instance, type) and
×
4876
            getattr(instance, "__IS_CONTAINER__", False) is True)
×
4877

4878

4879
cpdef object deepcopy(object instance, dict memo=None):
1✔
4880
    """Return full copy of provider or container with providers."""
4881
    if memo is None:
1✔
4882
        memo = dict()
1✔
4883

4884
    __add_sys_streams(memo)
1✔
4885

4886
    return copy.deepcopy(instance, memo)
1✔
4887

4888

4889
cpdef tuple deepcopy_args(
1✔
4890
    Provider provider,
4891
    tuple args,
4892
    dict[int, object] memo = None,
1✔
4893
):
4894
    """A wrapper for deepcopy for positional arguments.
4895

4896
    Used to improve debugability of objects that cannot be deep-copied.
4897
    """
4898

4899
    cdef list[object] out = []
1✔
4900

4901
    for i, arg in enumerate(args):
1✔
4902
        try:
1✔
4903
            out.append(copy.deepcopy(arg, memo))
1✔
4904
        except Exception as e:
1✔
4905
            raise NonCopyableArgumentError(provider, index=i) from e
1✔
4906

4907
    return tuple(out)
1✔
4908

4909

4910
cpdef dict[str, object] deepcopy_kwargs(
1✔
4911
    Provider provider,
4912
    dict[str, object] kwargs,
4913
    dict[int, object] memo = None,
1✔
4914
):
4915
    """A wrapper for deepcopy for keyword arguments.
4916

4917
    Used to improve debugability of objects that cannot be deep-copied.
4918
    """
4919

4920
    cdef dict[str, object] out = {}
1✔
4921

4922
    for name, arg in kwargs.items():
1✔
4923
        try:
1✔
4924
            out[name] = copy.deepcopy(arg, memo)
1✔
4925
        except Exception as e:
1✔
4926
            raise NonCopyableArgumentError(provider, keyword=name) from e
1✔
4927

4928
    return out
1✔
4929

4930

4931
def __add_sys_streams(memo):
1✔
4932
    """Add system streams to memo dictionary.
4933

4934
    This helps to avoid copying of system streams while making a deepcopy of
4935
    objects graph.
4936
    """
4937
    memo[id(sys.stdin)] = sys.stdin
1✔
4938
    memo[id(sys.stdout)] = sys.stdout
1✔
4939
    memo[id(sys.stderr)] = sys.stderr
1✔
4940

4941

4942
def merge_dicts(dict1, dict2):
1✔
4943
    """Merge dictionaries recursively.
4944

4945
    :param dict1: Dictionary 1
4946
    :type dict1: dict
4947

4948
    :param dict2: Dictionary 2
4949
    :type dict2: dict
4950

4951
    :return: New resulting dictionary
4952
    :rtype: dict
4953
    """
4954
    for key, value in dict1.items():
1✔
4955
        if key in dict2:
1✔
4956
            if isinstance(value, dict) and isinstance(dict2[key], dict):
1✔
4957
                dict2[key] = merge_dicts(value, dict2[key])
1✔
4958
    result = dict1.copy()
1✔
4959
    result.update(dict2)
1✔
4960
    return result
1✔
4961

4962

4963
def traverse(*providers, types=None):
1✔
4964
    """Return providers traversal generator."""
4965
    visited = set()
1✔
4966
    to_visit = set(providers)
1✔
4967

4968
    if types:
1✔
4969
        types = tuple(types)
1✔
4970

4971
    while len(to_visit) > 0:
1✔
4972
        visiting = to_visit.pop()
1✔
4973
        visited.add(visiting)
1✔
4974

4975
        for child in visiting.related:
1✔
4976
            if child in visited:
1✔
4977
                continue
1✔
4978
            to_visit.add(child)
1✔
4979

4980
        if types and not isinstance(visiting, types):
1✔
4981
            continue
1✔
4982

4983
        yield visiting
1✔
4984

4985

4986
def isawaitable(obj):
1✔
4987
    """Check if object is a coroutine function."""
4988
    try:
×
4989
        return inspect.isawaitable(obj)
×
4990
    except AttributeError:
×
4991
        return False
×
4992

4993

4994
def iscoroutinefunction(obj):
1✔
4995
    """Check if object is a coroutine function."""
4996
    try:
1✔
4997
        return inspect.iscoroutinefunction(obj)
1✔
4998
    except AttributeError:
×
4999
        return False
×
5000

5001

5002
def isasyncgenfunction(obj):
1✔
5003
    """Check if object is an asynchronous generator function."""
5004
    try:
1✔
5005
        return inspect.isasyncgenfunction(obj)
1✔
5006
    except AttributeError:
×
5007
        return False
×
5008

5009

5010
def _resolve_string_import(provides):
1✔
5011
    if provides is None:
1✔
5012
        return provides
1✔
5013

5014
    if not isinstance(provides, str):
1✔
5015
        return provides
1✔
5016

5017
    segments = provides.split(".")
1✔
5018
    member_name = segments[-1]
1✔
5019

5020
    if len(segments) == 1:
1✔
5021
        if  member_name in dir(builtins):
1✔
5022
            module = builtins
1✔
5023
        else:
5024
            module = _resolve_calling_module()
1✔
5025
        return getattr(module, member_name)
1✔
5026

5027
    module_name = ".".join(segments[:-1])
1✔
5028

5029
    package_name = _resolve_calling_package_name()
1✔
5030
    if module_name.startswith(".") and package_name is None:
1✔
5031
        raise ImportError("Attempted relative import with no known parent package")
×
5032

5033
    module = importlib.import_module(module_name, package=package_name)
1✔
5034
    return getattr(module, member_name)
1✔
5035

5036

5037
def _resolve_calling_module():
1✔
5038
    stack = inspect.stack()
1✔
5039
    pre_last_frame = stack[0]
1✔
5040
    return inspect.getmodule(pre_last_frame[0])
1✔
5041

5042

5043
def _resolve_calling_package_name():
1✔
5044
    module = _resolve_calling_module()
1✔
5045
    return module.__package__
1✔
5046

5047

5048
cpdef _copy_parent(object from_, object to, dict memo):
1✔
5049
    """Copy and assign provider parent."""
5050
    copied_parent = (
5051
        deepcopy(from_.parent, memo)
1✔
5052
        if is_provider(from_.parent) or is_container_instance(from_.parent)
1✔
5053
        else from_.parent
1✔
5054
    )
5055
    to.assign_parent(copied_parent)
1✔
5056

5057

5058
cpdef object _memorized_duplicate(object instance, dict memo):
1✔
5059
    copied = instance.__class__()
1✔
5060
    memo[id(instance)] = copied
1✔
5061
    return copied
1✔
5062

5063

5064
cpdef object _copy_if_provider(object instance, dict memo):
1✔
5065
    if not is_provider(instance):
1✔
5066
        return instance
1✔
5067
    return deepcopy(instance, memo)
1✔
5068

5069

5070
cpdef str _class_qualname(object instance):
1✔
5071
    name = getattr(instance.__class__, "__qualname__", None)
1✔
5072
    if not name:
1✔
5073
        name = ".".join((instance.__class__.__module__, instance.__class__.__name__))
×
5074
    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