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

ets-labs / python-dependency-injector / 12223222261

08 Dec 2024 03:58PM UTC coverage: 94.119% (-0.1%) from 94.261%
12223222261

Pull #837

github

ZipFile
Upgrade testing deps
Pull Request #837: Upgrade testing deps

3329 of 3537 relevant lines covered (94.12%)

0.94 hits per line

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

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

3
from __future__ import absolute_import
4

5
import copy
1✔
6
import errno
1✔
7
import functools
1✔
8
import importlib
1✔
9
import inspect
1✔
10
import json
1✔
11
import os
1✔
12
import re
1✔
13
import sys
1✔
14
import threading
1✔
15
import types
1✔
16
import warnings
1✔
17

18
try:
1✔
19
    import contextvars
1✔
20
except ImportError:
×
21
    contextvars = None
×
22

23
try:
1✔
24
    import builtins
1✔
25
except ImportError:
×
26
    # Python 2.7
27
    import __builtin__ as builtins
×
28

29
try:
1✔
30
    import asyncio
1✔
31
except ImportError:
×
32
    asyncio = None
×
33
    _is_coroutine_marker = None
×
34
else:
35
    if sys.version_info >= (3, 5, 3):
1✔
36
        import asyncio.coroutines
1✔
37
        _is_coroutine_marker = asyncio.coroutines._is_coroutine
1✔
38
    else:
39
        _is_coroutine_marker = True
×
40

41
try:
1✔
42
    import ConfigParser as iniconfigparser
1✔
43
except ImportError:
1✔
44
    import configparser as iniconfigparser
1✔
45

46
try:
1✔
47
    import yaml
1✔
48
except ImportError:
×
49
    yaml = None
×
50

51
has_pydantic_settings = True
1✔
52
cdef bint pydantic_v1 = False
1✔
53
cdef str pydantic_module = "pydantic_settings"
1✔
54
cdef str pydantic_extra = "pydantic2"
1✔
55

56
try:
1✔
57
    from pydantic_settings import BaseSettings as PydanticSettings
1✔
58
except ImportError:
×
59
    try:
×
60
        # pydantic-settings requires pydantic v2,
61
        # so it is safe to assume that we're dealing with v1:
62
        from pydantic import BaseSettings as PydanticSettings
×
63
        pydantic_v1 = True
×
64
        pydantic_module = "pydantic"
×
65
        pydantic_extra = "pydantic"
1✔
66
    except ImportError:
1✔
67
        # if it is present, ofc
68
        has_pydantic_settings = False
×
69

70

71
from .errors import (
1✔
72
    Error,
1✔
73
    NoSuchProviderError,
74
)
75

76
cimport cython
77

78

79
if sys.version_info[0] == 3:  # pragma: no cover
80
    CLASS_TYPES = (type,)
1✔
81
else:  # pragma: no cover
82
    CLASS_TYPES = (type, types.ClassType)
×
83

84
    copy._deepcopy_dispatch[types.MethodType] = \
×
85
        lambda obj, memo: type(obj)(obj.im_func,
×
86
                                    copy.deepcopy(obj.im_self, memo),
×
87
                                    obj.im_class)
×
88

89
if sys.version_info[:2] == (3, 5):
1✔
90
    warnings.warn(
×
91
        "Dependency Injector will drop support of Python 3.5 after Jan 1st of 2022. "
92
        "This does not mean that there will be any immediate breaking changes, "
93
        "but tests will no longer be executed on Python 3.5, and bugs will not be addressed.",
94
        category=DeprecationWarning,
×
95
    )
96

97
config_env_marker_pattern = re.compile(
1✔
98
    r"\${(?P<name>[^}^{:]+)(?P<separator>:?)(?P<default>.*?)}",
99
)
100

101
def _resolve_config_env_markers(config_content, envs_required=False):
1✔
102
    """Replace environment variable markers with their values."""
103
    findings = list(config_env_marker_pattern.finditer(config_content))
1✔
104

105
    for match in reversed(findings):
1✔
106
        env_name = match.group("name")
1✔
107
        has_default = match.group("separator") == ":"
1✔
108

109
        value = os.getenv(env_name)
1✔
110
        if value is None:
1✔
111
            if not has_default and envs_required:
1✔
112
                raise ValueError(f"Missing required environment variable \"{env_name}\"")
1✔
113
            value = match.group("default")
1✔
114

115
        span_min, span_max = match.span()
1✔
116
        config_content = f"{config_content[:span_min]}{value}{config_content[span_max:]}"
1✔
117
    return config_content
1✔
118

119

120
if sys.version_info[0] == 3:
1✔
121
    def _parse_ini_file(filepath, envs_required=False):
1✔
122
        parser = iniconfigparser.ConfigParser()
1✔
123
        with open(filepath) as config_file:
1✔
124
            config_string = _resolve_config_env_markers(
1✔
125
                config_file.read(),
1✔
126
                envs_required=envs_required,
1✔
127
            )
128
        parser.read_string(config_string)
1✔
129
        return parser
1✔
130
else:
131
    import StringIO
×
132

133
    def _parse_ini_file(filepath, envs_required=False):
×
134
        parser = iniconfigparser.ConfigParser()
×
135
        with open(filepath) as config_file:
×
136
            config_string = _resolve_config_env_markers(
×
137
                config_file.read(),
×
138
                envs_required=envs_required,
×
139
            )
140
        parser.readfp(StringIO.StringIO(config_string))
×
141
        return parser
×
142

143

144
if yaml:
1✔
145
    class YamlLoader(yaml.SafeLoader):
1✔
146
        """YAML loader.
147

148
        This loader mimics ``yaml.SafeLoader``.
149
        """
150
else:
151
    class YamlLoader:
×
152
        """YAML loader.
153

154
        This loader mimics ``yaml.SafeLoader``.
155
        """
156

157

158
UNDEFINED = object()
1✔
159

160
cdef int ASYNC_MODE_UNDEFINED = 0
1✔
161
cdef int ASYNC_MODE_ENABLED = 1
1✔
162
cdef int ASYNC_MODE_DISABLED = 2
1✔
163

164
cdef set __iscoroutine_typecache = set()
1✔
165
cdef tuple __COROUTINE_TYPES = asyncio.coroutines._COROUTINE_TYPES if asyncio else tuple()
1✔
166

167
cdef dict pydantic_settings_to_dict(settings, dict kwargs):
1✔
168
    if not has_pydantic_settings:
1✔
169
        raise Error(
1✔
170
            f"Unable to load pydantic configuration - {pydantic_module} is not installed. "
1✔
171
            "Install pydantic or install Dependency Injector with pydantic extras: "
172
            f"\"pip install dependency-injector[{pydantic_extra}]\""
1✔
173
        )
174

175
    if isinstance(settings, CLASS_TYPES) and issubclass(settings, PydanticSettings):
1✔
176
        raise Error(
1✔
177
            "Got settings class, but expect instance: "
178
            "instead \"{0}\" use \"{0}()\"".format(settings.__name__)
1✔
179
        )
180

181
    if not isinstance(settings, PydanticSettings):
1✔
182
        raise Error(
1✔
183
            f"Unable to recognize settings instance, expect \"{pydantic_module}.BaseSettings\", "
1✔
184
            f"got {settings} instead"
1✔
185
        )
186

187
    if pydantic_v1:
1✔
188
        return settings.dict(**kwargs)
×
189

190
    return settings.model_dump(mode="python", **kwargs)
1✔
191

192

193
cdef class Provider(object):
194
    """Base provider class.
195

196
    :py:class:`Provider` is callable (implements ``__call__`` method). Every
197
    call to provider object returns provided result, according to the providing
198
    strategy of particular provider. This ``callable`` functionality is a
199
    regular part of providers API and it should be the same for all provider
200
    subclasses.
201

202
    Implementation of particular providing strategy should be done in
203
    :py:meth:`Provider._provide` of :py:class:`Provider` subclass. Current
204
    method is called every time when not overridden provider is called.
205

206
    :py:class:`Provider` implements provider overriding logic that should be
207
    also common for all providers:
208

209
    .. code-block:: python
210

211
        provider1 = Factory(SomeClass)
212
        provider2 = Factory(ChildSomeClass)
213

214
        provider1.override(provider2)
215

216
        some_instance = provider1()
217
        assert isinstance(some_instance, ChildSomeClass)
218

219
    Also :py:class:`Provider` implements helper function for creating its
220
    delegates:
221

222
    .. code-block:: python
223

224
        provider = Factory(object)
225
        delegate = provider.delegate()
226

227
        delegated = delegate()
228

229
        assert provider is delegated
230

231
    All providers should extend this class.
232

233
    .. py:attribute:: overridden
234
       :noindex:
235

236
        Tuple of overriding providers, if any.
237

238
        :type: tuple[:py:class:`Provider`] | None
239
    """
240

241
    __IS_PROVIDER__ = True
1✔
242

243
    overriding_lock = threading.RLock()
1✔
244
    """Overriding reentrant lock.
245

246
    :type: :py:class:`threading.RLock`
247
    """
248

249
    def __init__(self):
250
        """Initializer."""
251
        self._overridden = tuple()
1✔
252
        self._last_overriding = None
1✔
253
        self._overrides = tuple()
1✔
254
        self._async_mode = ASYNC_MODE_UNDEFINED
1✔
255
        super(Provider, self).__init__()
1✔
256

257
    def __call__(self, *args, **kwargs):
258
        """Return provided object.
259

260
        Callable interface implementation.
261
        """
262
        if self._last_overriding is not None:
1✔
263
            result = self._last_overriding(*args, **kwargs)
1✔
264
        else:
265
            result = self._provide(args, kwargs)
1✔
266

267
        if self._async_mode == ASYNC_MODE_DISABLED:
1✔
268
            return result
1✔
269
        elif self._async_mode == ASYNC_MODE_ENABLED:
1✔
270
            if __is_future_or_coroutine(result):
1✔
271
                return result
1✔
272
            return __future_result(result)
1✔
273
        elif self._async_mode == ASYNC_MODE_UNDEFINED:
1✔
274
            if __is_future_or_coroutine(result):
1✔
275
                self.enable_async_mode()
1✔
276
            else:
277
                self.disable_async_mode()
1✔
278
            return result
1✔
279

280
    def __deepcopy__(self, memo):
1✔
281
        """Create and return full copy of provider."""
282
        copied = memo.get(id(self))
1✔
283
        if copied is not None:
1✔
284
            return copied
×
285

286
        copied = _memorized_duplicate(self, memo)
1✔
287
        self._copy_overridings(copied, memo)
1✔
288
        return copied
1✔
289

290
    @classmethod
1✔
291
    def __class_getitem__(cls, item):
292
        return cls
×
293

294
    def __str__(self):
295
        """Return string representation of provider.
296

297
        :rtype: str
298
        """
299
        return represent_provider(provider=self, provides=None)
1✔
300

301
    def __repr__(self):
302
        """Return string representation of provider.
303

304
        :rtype: str
305
        """
306
        return self.__str__()
1✔
307

308
    @property
309
    def overridden(self):
310
        """Return tuple of overriding providers."""
311
        with self.overriding_lock:
1✔
312
            return self._overridden
1✔
313

314
    @property
315
    def last_overriding(self):
316
        """Return last overriding provider.
317

318
        If provider is not overridden, then None is returned.
319
        """
320
        return self._last_overriding
1✔
321

322
    def override(self, provider):
1✔
323
        """Override provider with another provider.
324

325
        :param provider: Overriding provider.
326
        :type provider: :py:class:`Provider`
327

328
        :raise: :py:exc:`dependency_injector.errors.Error`
329

330
        :return: Overriding context.
331
        :rtype: :py:class:`OverridingContext`
332
        """
333
        if provider is self:
1✔
334
            raise Error("Provider {0} could not be overridden with itself".format(self))
1✔
335

336
        if not is_provider(provider):
1✔
337
            provider = Object(provider)
1✔
338

339
        with self.overriding_lock:
1✔
340
            self._overridden += (provider,)
1✔
341
            self._last_overriding = provider
1✔
342
            provider.register_overrides(self)
1✔
343

344
        return OverridingContext(self, provider)
1✔
345

346
    def reset_last_overriding(self):
1✔
347
        """Reset last overriding provider.
348

349
        :raise: :py:exc:`dependency_injector.errors.Error` if provider is not
350
                overridden.
351

352
        :rtype: None
353
        """
354
        with self.overriding_lock:
1✔
355
            if len(self._overridden) == 0:
1✔
356
                raise Error("Provider {0} is not overridden".format(str(self)))
1✔
357

358
            self._last_overriding.unregister_overrides(self)
1✔
359

360
            self._overridden = self._overridden[:-1]
1✔
361
            try:
1✔
362
                self._last_overriding = self._overridden[-1]
1✔
363
            except IndexError:
1✔
364
                self._last_overriding = None
1✔
365

366
    def reset_override(self):
1✔
367
        """Reset all overriding providers.
368

369
        :rtype: None
370
        """
371
        with self.overriding_lock:
1✔
372
            for provider in self._overridden:
1✔
373
                provider.unregister_overrides(self)
1✔
374
            self._overridden = tuple()
1✔
375
            self._last_overriding = None
1✔
376

377
    @property
378
    def overrides(self):
379
        """Return providers that are overridden by the current provider."""
380
        return self._overrides
1✔
381

382
    def register_overrides(self, provider):
1✔
383
        """Register provider that overrides current provider."""
384
        self._overrides =  tuple(set(self._overrides + (provider,)))
1✔
385

386
    def unregister_overrides(self, provider):
1✔
387
        """Unregister provider that overrides current provider."""
388
        overrides = set(self._overrides)
1✔
389
        if provider in overrides:
1✔
390
            overrides.remove(provider)
1✔
391
        self._overrides = tuple(overrides)
1✔
392

393
    def async_(self, *args, **kwargs):
1✔
394
        """Return provided object asynchronously.
395

396
        This method is a synonym of __call__().
397
        It provides typing stubs for correct type checking with
398
        `await` expression:
399

400
        .. code-block:: python
401

402
            database_provider: Provider[DatabaseConnection] = Resource(init_db_async)
403

404
            async def main():
405
                db: DatabaseConnection = await database_provider.async_()
406
                ...
407
        """
408
        return self.__call__(*args, **kwargs)
1✔
409

410
    def delegate(self):
1✔
411
        """Return provider delegate.
412

413
        :rtype: :py:class:`Delegate`
414
        """
415
        warnings.warn(
1✔
416
            "Method \".delegate()\" is deprecated since version 4.0.0. "
417
            "Use \".provider\" attribute instead.",
418
            category=DeprecationWarning,
1✔
419
        )
420
        return Delegate(self)
1✔
421

422
    @property
423
    def provider(self):
424
        """Return provider"s delegate.
425

426
        :rtype: :py:class:`Delegate`
427
        """
428
        return Delegate(self)
1✔
429

430
    @property
431
    def provided(self):
432
        """Return :py:class:`ProvidedInstance` provider."""
433
        return ProvidedInstance(self)
1✔
434

435
    def enable_async_mode(self):
1✔
436
        """Enable async mode."""
437
        self._async_mode = ASYNC_MODE_ENABLED
1✔
438

439
    def disable_async_mode(self):
1✔
440
        """Disable async mode."""
441
        self._async_mode = ASYNC_MODE_DISABLED
1✔
442

443
    def reset_async_mode(self):
1✔
444
        """Reset async mode.
445

446
        Provider will automatically set the mode on the next call.
447
        """
448
        self._async_mode = ASYNC_MODE_UNDEFINED
1✔
449

450
    cpdef bint is_async_mode_enabled(self):
1✔
451
        """Check if async mode is enabled."""
452
        return self._async_mode == ASYNC_MODE_ENABLED
1✔
453

454
    cpdef bint is_async_mode_disabled(self):
1✔
455
        """Check if async mode is disabled."""
456
        return self._async_mode == ASYNC_MODE_DISABLED
1✔
457

458
    cpdef bint is_async_mode_undefined(self):
1✔
459
        """Check if async mode is undefined."""
460
        return self._async_mode == ASYNC_MODE_UNDEFINED
1✔
461

462
    @property
463
    def related(self):
464
        """Return related providers generator."""
465
        yield from self.overridden
1✔
466

467
    def traverse(self, types=None):
1✔
468
        """Return providers traversal generator."""
469
        return traverse(*self.related, types=types)
1✔
470

471
    cpdef object _provide(self, tuple args, dict kwargs):
1✔
472
        """Providing strategy implementation.
473

474
        Abstract protected method that implements providing strategy of
475
        particular provider. Current method is called every time when not
476
        overridden provider is called. Need to be overridden in subclasses.
477
        """
478
        raise NotImplementedError()
1✔
479

480
    cpdef void _copy_overridings(self, Provider copied, dict memo):
1✔
481
        """Copy provider overridings to a newly copied provider."""
482
        copied._overridden = deepcopy(self._overridden, memo)
1✔
483
        copied._last_overriding = deepcopy(self._last_overriding, memo)
1✔
484
        copied._overrides = deepcopy(self._overrides, memo)
1✔
485

486

487
cdef class Object(Provider):
488
    """Object provider returns provided instance "as is".
489

490
    .. py:attribute:: provides
491

492
        Value that have to be provided.
493

494
        :type: object
495
    """
496

497
    def __init__(self, provides=None):
498
        """Initialize provider."""
499
        self._provides = None
1✔
500
        self.set_provides(provides)
1✔
501
        super(Object, self).__init__()
1✔
502

503
    def __deepcopy__(self, memo):
1✔
504
        """Create and return full copy of provider."""
505
        copied = memo.get(id(self))
1✔
506
        if copied is not None:
1✔
507
            return copied
×
508

509
        copied = _memorized_duplicate(self, memo)
1✔
510
        copied.set_provides(self.provides)
1✔
511

512
        self._copy_overridings(copied, memo)
1✔
513

514
        return copied
1✔
515

516
    def __str__(self):
517
        """Return string representation of provider.
518

519
        :rtype: str
520
        """
521
        return represent_provider(provider=self, provides=self._provides)
1✔
522

523
    def __repr__(self):
524
        """Return string representation of provider.
525

526
        :rtype: str
527
        """
528
        return self.__str__()
1✔
529

530
    @property
531
    def provides(self):
532
        """Return provider provides."""
533
        return self._provides
1✔
534

535
    def set_provides(self, provides):
1✔
536
        """Set provider provides."""
537
        self._provides = provides
1✔
538
        return self
1✔
539

540
    @property
541
    def related(self):
542
        """Return related providers generator."""
543
        if isinstance(self._provides, Provider):
1✔
544
            yield self._provides
1✔
545
        yield from super().related
1✔
546

547
    cpdef object _provide(self, tuple args, dict kwargs):
1✔
548
        """Return provided instance.
549

550
        :param args: Tuple of context positional arguments.
551
        :type args: tuple[object]
552

553
        :param kwargs: Dictionary of context keyword arguments.
554
        :type kwargs: dict[str, object]
555

556
        :rtype: object
557
        """
558
        return self._provides
1✔
559

560

561
cdef class Self(Provider):
562
    """Self provider returns own container."""
563

564
    def __init__(self, container=None):
565
        """Initialize provider."""
566
        self._container = container
1✔
567
        self._alt_names = tuple()
1✔
568
        super().__init__()
1✔
569

570
    def __deepcopy__(self, memo):
1✔
571
        """Create and return full copy of provider."""
572
        copied = memo.get(id(self))
1✔
573
        if copied is not None:
1✔
574
            return copied
×
575

576
        copied = _memorized_duplicate(self, memo)
1✔
577
        copied.set_container(deepcopy(self._container, memo))
1✔
578
        copied.set_alt_names(self._alt_names)
1✔
579
        self._copy_overridings(copied, memo)
1✔
580
        return copied
1✔
581

582
    def __str__(self):
583
        """Return string representation of provider.
584

585
        :rtype: str
586
        """
587
        return represent_provider(provider=self, provides=self._container)
1✔
588

589
    def __repr__(self):
590
        """Return string representation of provider.
591

592
        :rtype: str
593
        """
594
        return self.__str__()
1✔
595

596
    def set_container(self, container):
1✔
597
        self._container = container
1✔
598

599
    def set_alt_names(self, alt_names):
1✔
600
        self._alt_names = tuple(set(alt_names))
1✔
601

602
    @property
603
    def alt_names(self):
604
        return self._alt_names
1✔
605

606
    cpdef object _provide(self, tuple args, dict kwargs):
1✔
607
        return self._container
1✔
608

609

610
cdef class Delegate(Provider):
611
    """Delegate provider returns provider "as is".
612

613
    .. py:attribute:: provides
614

615
        Value that have to be provided.
616

617
        :type: object
618
    """
619

620
    def __init__(self, provides=None):
621
        """Initialize provider."""
622
        self._provides = None
1✔
623
        self.set_provides(provides)
1✔
624
        super(Delegate, self).__init__()
1✔
625

626
    def __deepcopy__(self, memo):
1✔
627
        """Create and return full copy of provider."""
628
        copied = memo.get(id(self))
1✔
629
        if copied is not None:
1✔
630
            return copied
×
631

632
        copied = _memorized_duplicate(self, memo)
1✔
633
        copied.set_provides(_copy_if_provider(self.provides, memo))
1✔
634
        self._copy_overridings(copied, memo)
1✔
635

636
        return copied
1✔
637

638
    def __str__(self):
639
        """Return string representation of provider.
640

641
        :rtype: str
642
        """
643
        return represent_provider(provider=self, provides=self._provides)
1✔
644

645
    def __repr__(self):
646
        """Return string representation of provider.
647

648
        :rtype: str
649
        """
650
        return self.__str__()
1✔
651

652
    @property
653
    def provides(self):
654
        """Return provider provides."""
655
        return self._provides
1✔
656

657
    def set_provides(self, provides):
1✔
658
        """Set provider provides."""
659
        if provides:
1✔
660
            provides = ensure_is_provider(provides)
1✔
661
        self._provides = provides
1✔
662
        return self
1✔
663

664
    @property
665
    def related(self):
666
        """Return related providers generator."""
667
        yield self._provides
1✔
668
        yield from super().related
1✔
669

670
    cpdef object _provide(self, tuple args, dict kwargs):
1✔
671
        """Return provided instance.
672

673
        :param args: Tuple of context positional arguments.
674
        :type args: tuple[object]
675

676
        :param kwargs: Dictionary of context keyword arguments.
677
        :type kwargs: dict[str, object]
678

679
        :rtype: object
680
        """
681
        return self._provides
1✔
682

683

684
cdef class Aggregate(Provider):
685
    """Providers aggregate.
686

687
    :py:class:`Aggregate` is a delegated provider, meaning that it is
688
    injected "as is".
689

690
    All aggregated providers can be retrieved as a read-only
691
    dictionary :py:attr:`Aggregate.providers` or as an attribute of
692
    :py:class:`Aggregate`, e.g. ``aggregate.provider``.
693
    """
694

695
    __IS_DELEGATED__ = True
1✔
696

697
    def __init__(self, provider_dict=None, **provider_kwargs):
698
        """Initialize provider."""
699
        self._providers = {}
1✔
700
        self.set_providers(provider_dict, **provider_kwargs)
1✔
701
        super().__init__()
1✔
702

703
    def __deepcopy__(self, memo):
1✔
704
        """Create and return full copy of provider."""
705
        copied = memo.get(id(self))
1✔
706
        if copied is not None:
1✔
707
            return copied
×
708

709
        copied = _memorized_duplicate(self, memo)
1✔
710
        copied.set_providers(deepcopy(self.providers, memo))
1✔
711

712
        self._copy_overridings(copied, memo)
1✔
713

714
        return copied
1✔
715

716
    def __getattr__(self, factory_name):
717
        """Return aggregated provider."""
718
        return self.__get_provider(factory_name)
1✔
719

720
    def __str__(self):
721
        """Return string representation of provider.
722

723
        :rtype: str
724
        """
725
        return represent_provider(provider=self, provides=self.providers)
1✔
726

727
    @property
728
    def providers(self):
729
        """Return dictionary of providers, read-only.
730

731
        Alias for ``.factories`` attribute.
732
        """
733
        return dict(self._providers)
1✔
734

735
    def set_providers(self, provider_dict=None, **provider_kwargs):
1✔
736
        """Set providers.
737

738
        Alias for ``.set_factories()`` method.
739
        """
740
        providers = {}
1✔
741
        providers.update(provider_kwargs)
1✔
742
        if provider_dict:
1✔
743
            providers.update(provider_dict)
1✔
744

745
        for provider in providers.values():
1✔
746
            if not is_provider(provider):
1✔
747
                raise Error(
1✔
748
                    "{0} can aggregate only instances of {1}, given - {2}".format(
1✔
749
                        self.__class__,
1✔
750
                        Provider,
751
                        provider,
1✔
752
                    ),
753
                )
754

755
        self._providers = providers
1✔
756
        return self
1✔
757

758
    def override(self, _):
1✔
759
        """Override provider with another provider.
760

761
        :raise: :py:exc:`dependency_injector.errors.Error`
762

763
        :return: Overriding context.
764
        :rtype: :py:class:`OverridingContext`
765
        """
766
        raise Error("{0} providers could not be overridden".format(self.__class__))
1✔
767

768
    @property
769
    def related(self):
770
        """Return related providers generator."""
771
        yield from self._providers.values()
1✔
772
        yield from super().related
1✔
773

774
    cpdef object _provide(self, tuple args, dict kwargs):
1✔
775
        try:
1✔
776
            provider_name = args[0]
1✔
777
        except IndexError:
1✔
778
            try:
1✔
779
                provider_name = kwargs.pop("factory_name")
1✔
780
            except KeyError:
1✔
781
                raise TypeError("Missing 1st required positional argument: \"provider_name\"")
1✔
782
        else:
783
            args = args[1:]
1✔
784

785
        return self.__get_provider(provider_name)(*args, **kwargs)
1✔
786

787
    cdef Provider __get_provider(self, object provider_name):
1✔
788
        if provider_name not in self._providers:
1✔
789
            raise NoSuchProviderError("{0} does not contain provider with name {1}".format(self, provider_name))
1✔
790
        return <Provider> self._providers[provider_name]
1✔
791

792

793
cdef class Dependency(Provider):
794
    """:py:class:`Dependency` provider describes dependency interface.
795

796
    This provider is used for description of dependency interface. That might
797
    be useful when dependency could be provided in the client"s code only,
798
    but its interface is known. Such situations could happen when required
799
    dependency has non-deterministic list of dependencies itself.
800

801
    .. code-block:: python
802

803
        database_provider = Dependency(sqlite3.dbapi2.Connection)
804
        database_provider.override(Factory(sqlite3.connect, ":memory:"))
805

806
        database = database_provider()
807

808
    .. py:attribute:: instance_of
809
       :noindex:
810

811
        Class of required dependency.
812

813
        :type: type
814
   """
815

816
    def __init__(self, object instance_of=object, default=None):
817
        """Initialize provider."""
818
        self._instance_of = None
1✔
819
        self.set_instance_of(instance_of)
1✔
820

821
        self._default = None
1✔
822
        self.set_default(default)
1✔
823

824
        self._parent = None
1✔
825

826
        super(Dependency, self).__init__()
1✔
827

828
    def __deepcopy__(self, memo):
1✔
829
        """Create and return full copy of provider."""
830
        copied = memo.get(id(self))
1✔
831
        if copied is not None:
1✔
832
            return copied
×
833

834
        copied = _memorized_duplicate(self, memo)
1✔
835
        copied.set_instance_of(self.instance_of)
1✔
836
        copied.set_default(deepcopy(self.default, memo))
1✔
837

838
        self._copy_parent(copied, memo)
1✔
839
        self._copy_overridings(copied, memo)
1✔
840

841
        return copied
1✔
842

843
    def __call__(self, *args, **kwargs):
844
        """Return provided instance.
845

846
        :raise: :py:exc:`dependency_injector.errors.Error`
847

848
        :rtype: object
849
        """
850
        if self._last_overriding:
1✔
851
            result = self._last_overriding(*args, **kwargs)
1✔
852
        elif self._default:
1✔
853
            result = self._default(*args, **kwargs)
1✔
854
        else:
855
            self._raise_undefined_error()
1✔
856

857
        if self._async_mode == ASYNC_MODE_DISABLED:
1✔
858
            self._check_instance_type(result)
1✔
859
            return result
1✔
860
        elif self._async_mode == ASYNC_MODE_ENABLED:
1✔
861
            if __is_future_or_coroutine(result):
1✔
862
                future_result = asyncio.Future()
1✔
863
                result = asyncio.ensure_future(result)
1✔
864
                result.add_done_callback(functools.partial(self._async_provide, future_result))
1✔
865
                return future_result
1✔
866
            else:
867
                self._check_instance_type(result)
1✔
868
                return __future_result(result)
1✔
869
        elif self._async_mode == ASYNC_MODE_UNDEFINED:
1✔
870
            if __is_future_or_coroutine(result):
1✔
871
                self.enable_async_mode()
1✔
872

873
                future_result = asyncio.Future()
1✔
874
                result = asyncio.ensure_future(result)
1✔
875
                result.add_done_callback(functools.partial(self._async_provide, future_result))
1✔
876
                return future_result
1✔
877
            else:
878
                self.disable_async_mode()
1✔
879
                self._check_instance_type(result)
1✔
880
                return result
1✔
881

882
    def __getattr__(self, name):
883
        if self._last_overriding:
1✔
884
            return getattr(self._last_overriding, name)
1✔
885
        elif self._default:
1✔
886
            return getattr(self._default, name)
1✔
887
        raise AttributeError(f"Provider \"{self.__class__.__name__}\" has no attribute \"{name}\"")
1✔
888

889
    def __str__(self):
890
        """Return string representation of provider.
891

892
        :rtype: str
893
        """
894
        name = f"<{self.__class__.__module__}.{self.__class__.__name__}"
1✔
895
        name += f"({repr(self._instance_of)}) at {hex(id(self))}"
1✔
896
        if self.parent_name:
1✔
897
            name += f", container name: \"{self.parent_name}\""
1✔
898
        name += f">"
1✔
899
        return name
1✔
900

901
    def __repr__(self):
902
        """Return string representation of provider.
903

904
        :rtype: str
905
        """
906
        return self.__str__()
1✔
907

908
    @property
909
    def instance_of(self):
910
        """Return type."""
911
        return self._instance_of
1✔
912

913
    def set_instance_of(self, instance_of):
1✔
914
        """Set type."""
915
        if not isinstance(instance_of, CLASS_TYPES):
1✔
916
            raise TypeError(
1✔
917
                "\"instance_of\" has incorrect type (expected {0}, got {1}))".format(
1✔
918
                    CLASS_TYPES,
919
                    instance_of,
1✔
920
                ),
921
            )
922
        self._instance_of = instance_of
1✔
923
        return self
1✔
924

925
    @property
926
    def default(self):
927
        """Return default provider."""
928
        return self._default
1✔
929

930
    def set_default(self, default):
1✔
931
        """Set type."""
932
        if default is not None and not isinstance(default, Provider):
1✔
933
            default = Object(default)
1✔
934
        self._default = default
1✔
935
        return self
1✔
936

937
    @property
938
    def is_defined(self):
939
        """Return True if dependency is defined."""
940
        return self._last_overriding is not None or self._default is not None
1✔
941

942
    def provided_by(self, provider):
1✔
943
        """Set external dependency provider.
944

945
        :param provider: Provider that provides required dependency.
946
        :type provider: :py:class:`Provider`
947

948
        :rtype: None
949
        """
950
        return self.override(provider)
1✔
951

952
    @property
953
    def related(self):
954
        """Return related providers generator."""
955
        if self._default:
1✔
956
            yield self._default
1✔
957
        yield from super().related
1✔
958

959
    @property
960
    def parent(self):
961
        """Return parent."""
962
        return self._parent
1✔
963

964
    @property
965
    def parent_name(self):
966
        """Return parent name."""
967
        if not self._parent:
1✔
968
            return None
1✔
969

970
        name = ""
1✔
971
        if self._parent.parent_name:
1✔
972
            name += f"{self._parent.parent_name}."
1✔
973
        name += f"{self._parent.resolve_provider_name(self)}"
1✔
974

975
        return name
1✔
976

977
    def assign_parent(self, parent):
1✔
978
        """Assign parent."""
979
        self._parent = parent
1✔
980

981
    def _copy_parent(self, copied, memo):
1✔
982
        _copy_parent(self, copied, memo)
1✔
983

984
    def _async_provide(self, future_result, future):
1✔
985
        try:
1✔
986
            instance = future.result()
1✔
987
            self._check_instance_type(instance)
1✔
988
        except Exception as exception:
1✔
989
            future_result.set_exception(exception)
1✔
990
        else:
991
            future_result.set_result(instance)
1✔
992

993
    def _check_instance_type(self, instance):
1✔
994
        if not isinstance(instance, self.instance_of):
1✔
995
            raise Error("{0} is not an instance of {1}".format(instance, self.instance_of))
1✔
996

997
    def _raise_undefined_error(self):
1✔
998
        if self.parent_name:
1✔
999
            raise Error(f"Dependency \"{self.parent_name}\" is not defined")
1✔
1000
        raise Error("Dependency is not defined")
1✔
1001

1002

1003
cdef class ExternalDependency(Dependency):
1004
    """:py:class:`ExternalDependency` provider describes dependency interface.
1005

1006
    This provider is used for description of dependency interface. That might
1007
    be useful when dependency could be provided in the client code only,
1008
    but its interface is known. Such situations could happen when required
1009
    dependency has non-deterministic list of dependencies itself.
1010

1011
    .. code-block:: python
1012

1013
        database_provider = ExternalDependency(sqlite3.dbapi2.Connection)
1014
        database_provider.override(Factory(sqlite3.connect, ":memory:"))
1015

1016
        database = database_provider()
1017

1018
    .. deprecated:: 3.9
1019

1020
        Use :py:class:`Dependency` instead.
1021

1022
    .. py:attribute:: instance_of
1023
       :noindex:
1024

1025
        Class of required dependency.
1026

1027
        :type: type
1028
    """
1029

1030

1031
cdef class DependenciesContainer(Object):
1032
    """:py:class:`DependenciesContainer` provider provides set of dependencies.
1033

1034

1035
    Dependencies container provider is used to implement late static binding
1036
    for a set of providers of a particular container.
1037

1038
    Example code:
1039

1040
    .. code-block:: python
1041

1042
        class Adapters(containers.DeclarativeContainer):
1043
            email_sender = providers.Singleton(SmtpEmailSender)
1044

1045
        class TestAdapters(containers.DeclarativeContainer):
1046
            email_sender = providers.Singleton(EchoEmailSender)
1047

1048
        class UseCases(containers.DeclarativeContainer):
1049
            adapters = providers.DependenciesContainer()
1050

1051
            signup = providers.Factory(SignupUseCase,
1052
                                       email_sender=adapters.email_sender)
1053

1054
        use_cases = UseCases(adapters=Adapters)
1055
        # or
1056
        use_cases = UseCases(adapters=TestAdapters)
1057

1058
        # Another file
1059
        from .containers import use_cases
1060

1061
        use_case = use_cases.signup()
1062
        use_case.execute()
1063
    """
1064

1065
    def __init__(self, **dependencies):
1066
        """Initializer."""
1067
        for provider in dependencies.values():
1✔
1068
            if isinstance(provider, CHILD_PROVIDERS):
1✔
1069
                provider.assign_parent(self)
1✔
1070

1071
        self._providers = dependencies
1✔
1072
        self._parent = None
1✔
1073

1074
        super(DependenciesContainer, self).__init__(None)
1✔
1075

1076
    def __deepcopy__(self, memo):
1✔
1077
        """Create and return full copy of provider."""
1078
        cdef DependenciesContainer copied
1079

1080
        copied = memo.get(id(self))
1✔
1081
        if copied is not None:
1✔
1082
            return copied
×
1083

1084
        copied = <DependenciesContainer> _memorized_duplicate(self, memo)
1✔
1085
        copied._provides = deepcopy(self._provides, memo)
1✔
1086
        copied._providers = deepcopy(self._providers, memo)
1✔
1087
        self._copy_parent(copied, memo)
1✔
1088
        self._copy_overridings(copied, memo)
1✔
1089

1090
        return copied
1✔
1091

1092
    def __getattr__(self, name):
1093
        """Return dependency provider."""
1094
        if name.startswith("__") and name.endswith("__"):
1✔
1095
            raise AttributeError(
1✔
1096
                "'{cls}' object has no attribute "
1097
                "'{attribute_name}'".format(cls=self.__class__.__name__, attribute_name=name)
1✔
1098
            )
1099

1100
        provider = self._providers.get(name)
1✔
1101
        if not provider:
1✔
1102
            provider = Dependency()
1✔
1103
            provider.assign_parent(self)
1✔
1104

1105
            self._providers[name] = provider
1✔
1106

1107
            container = self.__call__()
1✔
1108
            if container:
1✔
1109
                dependency_provider = container.providers.get(name)
1✔
1110
                if dependency_provider:
1✔
1111
                    provider.override(dependency_provider)
1✔
1112

1113
        return provider
1✔
1114

1115
    @property
1116
    def providers(self):
1117
        """Read-only dictionary of dependency providers."""
1118
        return self._providers
1✔
1119

1120
    def override(self, provider):
1✔
1121
        """Override provider with another provider.
1122

1123
        :param provider: Overriding provider.
1124
        :type provider: :py:class:`Provider`
1125

1126
        :raise: :py:exc:`dependency_injector.errors.Error`
1127

1128
        :return: Overriding context.
1129
        :rtype: :py:class:`OverridingContext`
1130
        """
1131
        self._override_providers(container=provider)
1✔
1132
        return super(DependenciesContainer, self).override(provider)
1✔
1133

1134
    def reset_last_overriding(self):
1✔
1135
        """Reset last overriding provider.
1136

1137
        :raise: :py:exc:`dependency_injector.errors.Error` if provider is not
1138
                overridden.
1139

1140
        :rtype: None
1141
        """
1142
        for child in self._providers.values():
1✔
1143
            try:
1✔
1144
                child.reset_last_overriding()
1✔
1145
            except Error:
×
1146
                pass
1147
        super(DependenciesContainer, self).reset_last_overriding()
1✔
1148

1149
    def reset_override(self):
1✔
1150
        """Reset all overriding providers.
1151

1152
        :rtype: None
1153
        """
1154
        for child in self._providers.values():
1✔
1155
            child.reset_override()
1✔
1156
        super(DependenciesContainer, self).reset_override()
1✔
1157

1158
    @property
1159
    def related(self):
1160
        """Return related providers generator."""
1161
        yield from self.providers.values()
1✔
1162
        yield from super().related
1✔
1163

1164
    def resolve_provider_name(self, provider):
1✔
1165
        """Try to resolve provider name."""
1166
        for provider_name, container_provider in self.providers.items():
1✔
1167
            if container_provider is provider:
1✔
1168
                return provider_name
1✔
1169
        else:
1170
            raise Error(f"Can not resolve name for provider \"{provider}\"")
1✔
1171

1172
    @property
1173
    def parent(self):
1174
        """Return parent."""
1175
        return self._parent
1✔
1176

1177
    @property
1178
    def parent_name(self):
1179
        """Return parent name."""
1180
        if not self._parent:
1✔
1181
            return None
1✔
1182

1183
        name = ""
1✔
1184
        if self._parent.parent_name:
1✔
1185
            name += f"{self._parent.parent_name}."
1✔
1186
        name += f"{self._parent.resolve_provider_name(self)}"
1✔
1187

1188
        return name
1✔
1189

1190
    def assign_parent(self, parent):
1✔
1191
        """Assign parent."""
1192
        self._parent = parent
1✔
1193

1194
    def _copy_parent(self, copied, memo):
1✔
1195
        _copy_parent(self, copied, memo)
1✔
1196

1197
    cpdef object _override_providers(self, object container):
1✔
1198
        """Override providers with providers from provided container."""
1199
        for name, dependency_provider in container.providers.items():
1✔
1200
            provider = getattr(self, name)
1✔
1201

1202
            if provider.last_overriding is dependency_provider:
1✔
1203
                continue
1✔
1204

1205
            provider.override(dependency_provider)
1✔
1206

1207

1208
cdef class Callable(Provider):
1209
    r"""Callable provider calls wrapped callable on every call.
1210

1211
    Callable supports positional and keyword argument injections:
1212

1213
    .. code-block:: python
1214

1215
        some_function = Callable(some_function,
1216
                                 "positional_arg1", "positional_arg2",
1217
                                 keyword_argument1=3, keyword_argument=4)
1218

1219
        # or
1220

1221
        some_function = Callable(some_function) \
1222
            .add_args("positional_arg1", "positional_arg2") \
1223
            .add_kwargs(keyword_argument1=3, keyword_argument=4)
1224

1225
        # or
1226

1227
        some_function = Callable(some_function)
1228
        some_function.add_args("positional_arg1", "positional_arg2")
1229
        some_function.add_kwargs(keyword_argument1=3, keyword_argument=4)
1230
    """
1231

1232
    def __init__(self, provides=None, *args, **kwargs):
1233
        """Initialize provider."""
1234
        self._provides = None
1✔
1235
        self.set_provides(provides)
1✔
1236

1237
        self._args = tuple()
1✔
1238
        self._args_len = 0
1✔
1239
        self.set_args(*args)
1✔
1240

1241
        self._kwargs = tuple()
1✔
1242
        self._kwargs_len = 0
1✔
1243
        self.set_kwargs(**kwargs)
1✔
1244

1245
        super(Callable, self).__init__()
1✔
1246

1247
    def __deepcopy__(self, memo):
1✔
1248
        """Create and return full copy of provider."""
1249
        copied = memo.get(id(self))
1✔
1250
        if copied is not None:
1✔
1251
            return copied
×
1252

1253
        copied = _memorized_duplicate(self, memo)
1✔
1254
        copied.set_provides(_copy_if_provider(self.provides, memo))
1✔
1255
        copied.set_args(*deepcopy(self.args, memo))
1✔
1256
        copied.set_kwargs(**deepcopy(self.kwargs, memo))
1✔
1257
        self._copy_overridings(copied, memo)
1✔
1258
        return copied
1✔
1259

1260
    def __str__(self):
1261
        """Return string representation of provider.
1262

1263
        :rtype: str
1264
        """
1265
        return represent_provider(provider=self, provides=self._provides)
1✔
1266

1267
    @property
1268
    def provides(self):
1269
        """Return provider provides."""
1270
        return self._provides
1✔
1271

1272
    def set_provides(self, provides):
1✔
1273
        """Set provider provides."""
1274
        provides = _resolve_string_import(provides)
1✔
1275
        if provides and not callable(provides):
1✔
1276
            raise Error(
1✔
1277
                "Provider {0} expected to get callable, got {1} instead".format(
1✔
1278
                    _class_qualname(self),
1✔
1279
                    provides,
1✔
1280
                ),
1281
            )
1282
        self._provides = provides
1✔
1283
        return self
1✔
1284

1285
    @property
1286
    def args(self):
1287
        """Return positional argument injections."""
1288
        cdef int index
1289
        cdef PositionalInjection arg
1290
        cdef list args
1291

1292
        args = list()
1✔
1293
        for index in range(self._args_len):
1✔
1294
            arg = self._args[index]
1✔
1295
            args.append(arg._value)
1✔
1296
        return tuple(args)
1✔
1297

1298
    def add_args(self, *args):
1✔
1299
        """Add positional argument injections.
1300

1301
        :return: Reference ``self``
1302
        """
1303
        self._args += parse_positional_injections(args)
1✔
1304
        self._args_len = len(self._args)
1✔
1305
        return self
1✔
1306

1307
    def set_args(self, *args):
1✔
1308
        """Set positional argument injections.
1309

1310
        Existing positional argument injections are dropped.
1311

1312
        :return: Reference ``self``
1313
        """
1314
        self._args = parse_positional_injections(args)
1✔
1315
        self._args_len = len(self._args)
1✔
1316
        return self
1✔
1317

1318
    def clear_args(self):
1✔
1319
        """Drop positional argument injections.
1320

1321
        :return: Reference ``self``
1322
        """
1323
        self._args = tuple()
1✔
1324
        self._args_len = len(self._args)
1✔
1325
        return self
1✔
1326

1327
    @property
1328
    def kwargs(self):
1329
        """Return keyword argument injections."""
1330
        cdef int index
1331
        cdef NamedInjection kwarg
1332
        cdef dict kwargs
1333

1334
        kwargs = dict()
1✔
1335
        for index in range(self._kwargs_len):
1✔
1336
            kwarg = self._kwargs[index]
1✔
1337
            kwargs[kwarg._name] = kwarg._value
1✔
1338
        return kwargs
1✔
1339

1340
    def add_kwargs(self, **kwargs):
1✔
1341
        """Add keyword argument injections.
1342

1343
        :return: Reference ``self``
1344
        """
1345
        self._kwargs += parse_named_injections(kwargs)
1✔
1346
        self._kwargs_len = len(self._kwargs)
1✔
1347
        return self
1✔
1348

1349
    def set_kwargs(self, **kwargs):
1✔
1350
        """Set keyword argument injections.
1351

1352
        Existing keyword argument injections are dropped.
1353

1354
        :return: Reference ``self``
1355
        """
1356
        self._kwargs = parse_named_injections(kwargs)
1✔
1357
        self._kwargs_len = len(self._kwargs)
1✔
1358
        return self
1✔
1359

1360
    def clear_kwargs(self):
1✔
1361
        """Drop keyword argument injections.
1362

1363
        :return: Reference ``self``
1364
        """
1365
        self._kwargs = tuple()
1✔
1366
        self._kwargs_len = len(self._kwargs)
1✔
1367
        return self
1✔
1368

1369
    @property
1370
    def related(self):
1371
        """Return related providers generator."""
1372
        yield from filter(is_provider, [self.provides])
1✔
1373
        yield from filter(is_provider, self.args)
1✔
1374
        yield from filter(is_provider, self.kwargs.values())
1✔
1375
        yield from super().related
1✔
1376

1377
    cpdef object _provide(self, tuple args, dict kwargs):
1✔
1378
        """Return result of provided callable call."""
1379
        return __callable_call(self, args, kwargs)
1✔
1380

1381

1382
cdef class DelegatedCallable(Callable):
1383
    """Callable that is injected "as is".
1384

1385
    DelegatedCallable is a :py:class:`Callable`, that is injected "as is".
1386
    """
1387

1388
    __IS_DELEGATED__ = True
1✔
1389

1390

1391
cdef class AbstractCallable(Callable):
1392
    """Abstract callable provider.
1393

1394
    :py:class:`AbstractCallable` is a :py:class:`Callable` provider that must
1395
    be explicitly overridden before calling.
1396

1397
    Overriding of :py:class:`AbstractCallable` is possible only by another
1398
    :py:class:`Callable` provider.
1399
    """
1400

1401
    def __call__(self, *args, **kwargs):
1402
        """Return provided object.
1403

1404
        Callable interface implementation.
1405
        """
1406
        if self._last_overriding is None:
1✔
1407
            raise Error("{0} must be overridden before calling".format(self))
1✔
1408
        return super().__call__(*args, **kwargs)
1✔
1409

1410
    def override(self, provider):
1✔
1411
        """Override provider with another provider.
1412

1413
        :param provider: Overriding provider.
1414
        :type provider: :py:class:`Provider`
1415

1416
        :raise: :py:exc:`dependency_injector.errors.Error`
1417

1418
        :return: Overriding context.
1419
        :rtype: :py:class:`OverridingContext`
1420
        """
1421
        if not isinstance(provider, Callable):
1✔
1422
            raise Error("{0} must be overridden only by "
1✔
1423
                        "{1} providers".format(self, Callable))
1✔
1424
        return super(AbstractCallable, self).override(provider)
1✔
1425

1426
    cpdef object _provide(self, tuple args, dict kwargs):
1✔
1427
        """Return result of provided callable"s call."""
1428
        raise NotImplementedError("Abstract provider forward providing logic "
1✔
1429
                                  "to overriding provider")
1430

1431

1432
cdef class CallableDelegate(Delegate):
1433
    """Callable delegate injects delegating callable "as is".
1434

1435
    .. py:attribute:: provides
1436

1437
        Value that have to be provided.
1438

1439
        :type: object
1440
    """
1441

1442
    def __init__(self, callable):
1443
        """Initializer.
1444

1445
        :param callable: Value that have to be provided.
1446
        :type callable: object
1447
        """
1448
        if isinstance(callable, Callable) is False:
1✔
1449
            raise Error("{0} can wrap only {1} providers".format(self.__class__, Callable))
1✔
1450
        super(CallableDelegate, self).__init__(callable)
1✔
1451

1452

1453
cdef class Coroutine(Callable):
1454
    r"""Coroutine provider creates wrapped coroutine on every call.
1455

1456
    Coroutine supports positional and keyword argument injections:
1457

1458
    .. code-block:: python
1459

1460
        some_coroutine = Coroutine(some_coroutine,
1461
                                   "positional_arg1", "positional_arg2",
1462
                                   keyword_argument1=3, keyword_argument=4)
1463

1464
        # or
1465

1466
        some_coroutine = Coroutine(some_coroutine) \
1467
            .add_args("positional_arg1", "positional_arg2") \
1468
            .add_kwargs(keyword_argument1=3, keyword_argument=4)
1469

1470
        # or
1471

1472
        some_coroutine = Coroutine(some_coroutine)
1473
        some_coroutine.add_args("positional_arg1", "positional_arg2")
1474
        some_coroutine.add_kwargs(keyword_argument1=3, keyword_argument=4)
1475
    """
1476

1477
    _is_coroutine = _is_coroutine_marker
1✔
1478

1479
    def set_provides(self, provides):
1✔
1480
        """Set provider provides."""
1481
        if not asyncio:
1✔
1482
            raise Error("Package asyncio is not available")
×
1483
        provides = _resolve_string_import(provides)
1✔
1484
        if provides and not asyncio.iscoroutinefunction(provides):
1✔
1485
            raise Error(f"Provider {_class_qualname(self)} expected to get coroutine function, "
1✔
1486
                        f"got {provides} instead")
1✔
1487
        return super().set_provides(provides)
1✔
1488

1489

1490
cdef class DelegatedCoroutine(Coroutine):
1491
    """Coroutine provider that is injected "as is".
1492

1493
    DelegatedCoroutine is a :py:class:`Coroutine`, that is injected "as is".
1494
    """
1495

1496
    __IS_DELEGATED__ = True
1✔
1497

1498

1499
cdef class AbstractCoroutine(Coroutine):
1500
    """Abstract coroutine provider.
1501

1502
    :py:class:`AbstractCoroutine` is a :py:class:`Coroutine` provider that must
1503
    be explicitly overridden before calling.
1504

1505
    Overriding of :py:class:`AbstractCoroutine` is possible only by another
1506
    :py:class:`Coroutine` provider.
1507
    """
1508

1509
    def __call__(self, *args, **kwargs):
1510
        """Return provided object.
1511

1512
        Callable interface implementation.
1513
        """
1514
        if self._last_overriding is None:
1✔
1515
            raise Error("{0} must be overridden before calling".format(self))
1✔
1516
        return super().__call__(*args, **kwargs)
×
1517

1518
    def override(self, provider):
1✔
1519
        """Override provider with another provider.
1520

1521
        :param provider: Overriding provider.
1522
        :type provider: :py:class:`Provider`
1523

1524
        :raise: :py:exc:`dependency_injector.errors.Error`
1525

1526
        :return: Overriding context.
1527
        :rtype: :py:class:`OverridingContext`
1528
        """
1529
        if not isinstance(provider, Coroutine):
1✔
1530
            raise Error("{0} must be overridden only by "
1✔
1531
                        "{1} providers".format(self, Coroutine))
1✔
1532
        return super(AbstractCoroutine, self).override(provider)
×
1533

1534
    cpdef object _provide(self, tuple args, dict kwargs):
1✔
1535
        """Return result of provided callable"s call."""
1536
        raise NotImplementedError("Abstract provider forward providing logic "
1✔
1537
                                  "to overriding provider")
1538

1539

1540
cdef class CoroutineDelegate(Delegate):
1541
    """Coroutine delegate injects delegating coroutine "as is".
1542

1543
    .. py:attribute:: provides
1544

1545
        Value that have to be provided.
1546

1547
        :type: object
1548
    """
1549

1550
    def __init__(self, coroutine):
1551
        """Initializer.
1552

1553
        :param coroutine: Value that have to be provided.
1554
        :type coroutine: object
1555
        """
1556
        if isinstance(coroutine, Coroutine) is False:
1✔
1557
            raise Error("{0} can wrap only {1} providers".format(self.__class__, Callable))
1✔
1558
        super(CoroutineDelegate, self).__init__(coroutine)
1✔
1559

1560

1561
cdef class ConfigurationOption(Provider):
1562
    """Child configuration option provider.
1563

1564
    This provider should not be used directly. It is a part of the
1565
    :py:class:`Configuration` provider.
1566
    """
1567

1568
    def __init__(self, name=None, Configuration root=None, required=False):
1569
        self._name = name
1✔
1570
        self._root = root
1✔
1571
        self._children = {}
1✔
1572
        self._required = required
1✔
1573
        self._cache = UNDEFINED
1✔
1574
        super().__init__()
1✔
1575

1576
    def __deepcopy__(self, memo):
1✔
1577
        cdef ConfigurationOption copied
1578

1579
        copied = memo.get(id(self))
1✔
1580
        if copied is not None:
1✔
1581
            return copied
×
1582

1583
        copied = <ConfigurationOption> _memorized_duplicate(self, memo)
1✔
1584
        copied._name = deepcopy(self._name, memo)
1✔
1585
        copied._root = deepcopy(self._root, memo)
1✔
1586
        copied._children = deepcopy(self._children, memo)
1✔
1587
        copied._required = self._required
1✔
1588
        self._copy_overridings(copied, memo)
1✔
1589
        return copied
1✔
1590

1591
    def __enter__(self):
1✔
1592
        return self
1✔
1593

1594
    def __exit__(self, *exc_info):
1✔
1595
        pass
1596

1597
    def __str__(self):
1598
        return represent_provider(provider=self, provides=self.get_name())
1✔
1599

1600
    def __getattr__(self, item):
1601
        if item.startswith("__") and item.endswith("__"):
1✔
1602
            raise AttributeError(
1✔
1603
                "'{cls}' object has no attribute "
1604
                "'{attribute_name}'".format(cls=self.__class__.__name__, attribute_name=item)
1✔
1605
            )
1606

1607
        child = self._children.get(item)
1✔
1608
        if child is None:
1✔
1609
            child_name = self._name + (item,)
1✔
1610
            child = ConfigurationOption(child_name, self._root)
1✔
1611
            self._children[item] = child
1✔
1612
        return child
1✔
1613

1614
    def __getitem__(self, item):
1615
        child = self._children.get(item)
1✔
1616
        if child is None:
1✔
1617
            child_name = self._name + (item,)
1✔
1618
            child = ConfigurationOption(child_name, self._root)
1✔
1619
            self._children[item] = child
1✔
1620
        return child
1✔
1621

1622
    cpdef object _provide(self, tuple args, dict kwargs):
1✔
1623
        """Return new instance."""
1624
        if self._cache is not UNDEFINED:
1✔
1625
            return self._cache
1✔
1626

1627
        value = self._root.get(self._get_self_name(), self._required)
1✔
1628
        self._cache = value
1✔
1629
        return value
1✔
1630

1631
    def _get_self_name(self):
1✔
1632
        return ".".join(
1✔
1633
            segment() if is_provider(segment) else segment for segment in self._name
1✔
1634
        )
1635

1636
    @property
1637
    def root(self):
1638
        return self._root
1✔
1639

1640
    def get_name(self):
1✔
1641
        return ".".join((self._root.get_name(), self._get_self_name()))
1✔
1642

1643
    def get_name_segments(self):
1✔
1644
        return self._name
1✔
1645

1646
    def as_int(self):
1✔
1647
        return TypedConfigurationOption(int, self)
1✔
1648

1649
    def as_float(self):
1✔
1650
        return TypedConfigurationOption(float, self)
1✔
1651

1652
    def as_(self, callback, *args, **kwargs):
1✔
1653
        return TypedConfigurationOption(callback, self, *args, **kwargs)
1✔
1654

1655
    def required(self):
1✔
1656
        return self.__class__(self._name, self._root, required=True)
1✔
1657

1658
    def is_required(self):
1✔
1659
        return self._required
1✔
1660

1661
    def override(self, value):
1✔
1662
        if isinstance(value, Provider):
1✔
1663
            raise Error("Configuration option can only be overridden by a value")
×
1664
        return self._root.set(self._get_self_name(), value)
1✔
1665

1666
    def reset_last_overriding(self):
1✔
1667
        raise Error("Configuration option does not support this method")
×
1668

1669
    def reset_override(self):
1✔
1670
        raise Error("Configuration option does not support this method")
×
1671

1672
    def reset_cache(self):
1✔
1673
        self._cache = UNDEFINED
1✔
1674

1675
        for provider in self._children.values():
1✔
1676
            provider.reset_cache()
1✔
1677

1678
        for provider in self.overrides:
1✔
1679
            if isinstance(provider, (Configuration, ConfigurationOption)):
1✔
1680
                provider.reset_cache()
1✔
1681

1682
    def update(self, value):
1✔
1683
        """Set configuration options.
1684

1685
        .. deprecated:: 3.11
1686

1687
            Use :py:meth:`Configuration.override` instead.
1688

1689
        :param value: Value of configuration option.
1690
        :type value: object | dict
1691

1692
        :rtype: None
1693
        """
1694
        self.override(value)
×
1695

1696
    def from_ini(self, filepath, required=UNDEFINED, envs_required=UNDEFINED):
1✔
1697
        """Load configuration from the ini file.
1698

1699
        Loaded configuration is merged recursively over existing configuration.
1700

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

1704
        :param required: When required is True, raise an exception if file does not exist.
1705
        :type required: bool
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
        try:
1✔
1713
            parser = _parse_ini_file(
1✔
1714
                filepath,
1✔
1715
                envs_required=envs_required if envs_required is not UNDEFINED else self._is_strict_mode_enabled(),
1✔
1716
            )
1717
        except IOError as exception:
1✔
1718
            if required is not False \
1✔
1719
                    and (self._is_strict_mode_enabled() or required is True) \
1✔
1720
                    and exception.errno in (errno.ENOENT, errno.EISDIR):
1✔
1721
                exception.strerror = "Unable to load configuration file {0}".format(exception.strerror)
1✔
1722
                raise
1✔
1723
            return
1✔
1724

1725
        config = {}
1✔
1726
        for section in parser.sections():
1✔
1727
            config[section] = dict(parser.items(section))
1✔
1728

1729
        current_config = self.__call__()
1✔
1730
        if not current_config:
1✔
1731
            current_config = {}
1✔
1732
        self.override(merge_dicts(current_config, config))
1✔
1733

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

1737
        Loaded configuration is merged recursively over existing configuration.
1738

1739
        :param filepath: Path to the configuration file.
1740
        :type filepath: str
1741

1742
        :param required: When required is True, raise an exception if file does not exist.
1743
        :type required: bool
1744

1745
        :param loader: YAML loader, :py:class:`YamlLoader` is used if not specified.
1746
        :type loader: ``yaml.Loader``
1747

1748
        :param envs_required: When True, raises an error on undefined environment variable.
1749
        :type envs_required: bool
1750

1751
        :rtype: None
1752
        """
1753
        if yaml is None:
1✔
1754
            raise Error(
1✔
1755
                "Unable to load yaml configuration - PyYAML is not installed. "
1756
                "Install PyYAML or install Dependency Injector with yaml extras: "
1757
                "\"pip install dependency-injector[yaml]\""
1758
            )
1759

1760
        if loader is None:
1✔
1761
            loader = YamlLoader
1✔
1762

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

1774
        config_content = _resolve_config_env_markers(
1✔
1775
            config_content,
1✔
1776
            envs_required=envs_required if envs_required is not UNDEFINED else self._is_strict_mode_enabled(),
1✔
1777
        )
1778
        config = yaml.load(config_content, loader)
1✔
1779

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

1785
    def from_json(self, filepath, required=UNDEFINED, envs_required=UNDEFINED):
1✔
1786
        """Load configuration from a json file.
1787

1788
        Loaded configuration is merged recursively over the existing configuration.
1789

1790
        :param filepath: Path to a configuration file.
1791
        :type filepath: str
1792

1793
        :param required: When required is True, raise an exception if file does not exist.
1794
        :type required: bool
1795

1796
        :param envs_required: When True, raises an exception on undefined environment variable.
1797
        :type envs_required: bool
1798

1799
        :rtype: None
1800
        """
1801
        try:
1✔
1802
            with open(filepath) as opened_file:
1✔
1803
                config_content = opened_file.read()
1✔
1804
        except IOError as exception:
1✔
1805
            if required is not False \
1✔
1806
                    and (self._is_strict_mode_enabled() or required is True) \
1✔
1807
                    and exception.errno in (errno.ENOENT, errno.EISDIR):
1✔
1808
                exception.strerror = "Unable to load configuration file {0}".format(exception.strerror)
1✔
1809
                raise
1✔
1810
            return
1✔
1811

1812
        config_content = _resolve_config_env_markers(
1✔
1813
            config_content,
1✔
1814
            envs_required=envs_required if envs_required is not UNDEFINED else self._is_strict_mode_enabled(),
1✔
1815
        )
1816
        config = json.loads(config_content)
1✔
1817

1818
        current_config = self.__call__()
1✔
1819
        if not current_config:
1✔
1820
            current_config = {}
1✔
1821
        self.override(merge_dicts(current_config, config))
1✔
1822

1823
    def from_pydantic(self, settings, required=UNDEFINED, **kwargs):
1✔
1824
        """Load configuration from pydantic settings.
1825

1826
        Loaded configuration is merged recursively over existing configuration.
1827

1828
        :param settings: Pydantic settings instances.
1829
        :type settings: :py:class:`pydantic.BaseSettings` (pydantic v1) or
1830
            :py:class:`pydantic_settings.BaseSettings` (pydantic v2 and onwards)
1831

1832
        :param required: When required is True, raise an exception if settings dict is empty.
1833
        :type required: bool
1834

1835
        :param kwargs: Keyword arguments forwarded to ``pydantic.BaseSettings.dict()`` or
1836
            ``pydantic_settings.BaseSettings.model_dump()`` call (based on pydantic version).
1837
        :type kwargs: Dict[Any, Any]
1838

1839
        :rtype: None
1840
        """
1841

1842
        self.from_dict(pydantic_settings_to_dict(settings, kwargs), required=required)
1✔
1843

1844
    def from_dict(self, options, required=UNDEFINED):
1✔
1845
        """Load configuration from the dictionary.
1846

1847
        Loaded configuration is merged recursively over existing configuration.
1848

1849
        :param options: Configuration options.
1850
        :type options: dict
1851

1852
        :param required: When required is True, raise an exception if dictionary is empty.
1853
        :type required: bool
1854

1855
        :rtype: None
1856
        """
1857
        if required is not False \
1✔
1858
                and (self._is_strict_mode_enabled() or required is True) \
1✔
1859
                and not options:
1✔
1860
            raise ValueError("Can not use empty dictionary")
1✔
1861

1862
        try:
1✔
1863
            current_config = self.__call__()
1✔
1864
        except Error:
1✔
1865
            current_config = {}
1✔
1866
        else:
1867
            if not current_config:
1✔
1868
                current_config = {}
1✔
1869

1870
        self.override(merge_dicts(current_config, options))
1✔
1871

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

1875
        :param name: Name of the environment variable.
1876
        :type name: str
1877

1878
        :param default: Default value that is used if environment variable does not exist.
1879
        :type default: object
1880

1881
        :param required: When required is True, raise an exception if environment variable is undefined.
1882
        :type required: bool
1883

1884
        :param as_: Callable used for type casting (int, float, etc).
1885
        :type as_: object
1886

1887
        :rtype: None
1888
        """
1889
        value = os.environ.get(name, default)
1✔
1890

1891
        if value is UNDEFINED:
1✔
1892
            if required is not False \
1✔
1893
                    and (self._is_strict_mode_enabled() or required is True):
1✔
1894
                raise ValueError("Environment variable \"{0}\" is undefined".format(name))
1✔
1895
            value = None
1✔
1896

1897
        if as_ is not UNDEFINED:
1✔
1898
            value = as_(value)
1✔
1899

1900
        self.override(value)
1✔
1901

1902
    def from_value(self, value):
1✔
1903
        """Load configuration value.
1904

1905
        :param value: Configuration value
1906
        :type value: object
1907

1908
        :rtype: None
1909
        """
1910
        self.override(value)
1✔
1911

1912
    @property
1913
    def related(self):
1914
        """Return related providers generator."""
1915
        yield from filter(is_provider, self._name)
1✔
1916
        yield from self._children.values()
1✔
1917
        yield from super().related
1✔
1918

1919
    def _is_strict_mode_enabled(self):
1✔
1920
        return self._root.__strict
1✔
1921

1922

1923
cdef class TypedConfigurationOption(Callable):
1924

1925
    @property
1926
    def option(self):
1927
        return self.args[0]
1✔
1928

1929

1930
cdef class Configuration(Object):
1931
    """Configuration provider provides configuration options to the other providers.
1932

1933
    .. code-block:: python
1934

1935
        config = Configuration("config")
1936
        print(config.section1.option1())  # None
1937
        print(config.section1.option2())  # None
1938
        config.from_dict(
1939
            {
1940
                "section1": {
1941
                    "option1": 1,
1942
                    "option2": 2,
1943
                },
1944
            },
1945
        )
1946
        print(config.section1.option1())  # 1
1947
        print(config.section1.option2())  # 2
1948
    """
1949

1950
    DEFAULT_NAME = "config"
1✔
1951

1952
    def __init__(self, name=DEFAULT_NAME, default=None, strict=False, ini_files=None, yaml_files=None, json_files=None, pydantic_settings=None):
1✔
1953
        self._name = name
1✔
1954
        self.__strict = strict
1✔
1955
        self._children = {}
1✔
1956
        self._ini_files = []
1✔
1957
        self._yaml_files = []
1✔
1958
        self._json_files = []
1✔
1959
        self._pydantic_settings = []
1✔
1960

1961
        super().__init__(provides={})
1✔
1962
        self.set_default(default)
1✔
1963

1964
        if ini_files is None:
1✔
1965
            ini_files = []
1✔
1966
        self.set_ini_files(ini_files)
1✔
1967

1968
        if yaml_files is None:
1✔
1969
            yaml_files = []
1✔
1970
        self.set_yaml_files(yaml_files)
1✔
1971

1972
        if json_files is None:
1✔
1973
            json_files = []
1✔
1974
        self.set_json_files(json_files)
1✔
1975

1976
        if pydantic_settings is None:
1✔
1977
            pydantic_settings = []
1✔
1978
        self.set_pydantic_settings(pydantic_settings)
1✔
1979

1980
    def __deepcopy__(self, memo):
1✔
1981
        copied = memo.get(id(self))
1✔
1982
        if copied is not None:
1✔
1983
            return copied
×
1984

1985
        copied = _memorized_duplicate(self, memo)
1✔
1986
        copied.set_name(self.get_name())
1✔
1987
        copied.set_default(self.get_default())
1✔
1988
        copied.set_strict(self.get_strict())
1✔
1989
        copied.set_children(deepcopy(self.get_children(), memo))
1✔
1990
        copied.set_ini_files(self.get_ini_files())
1✔
1991
        copied.set_yaml_files(self.get_yaml_files())
1✔
1992
        copied.set_json_files(self.get_json_files())
1✔
1993
        copied.set_pydantic_settings(self.get_pydantic_settings())
1✔
1994

1995
        self._copy_overridings(copied, memo)
1✔
1996
        return copied
1✔
1997

1998
    def __enter__(self):
1✔
1999
        return self
1✔
2000

2001
    def __exit__(self, *exc_info):
1✔
2002
        pass
2003

2004
    def __str__(self):
2005
        return represent_provider(provider=self, provides=self._name)
1✔
2006

2007
    def __getattr__(self, item):
2008
        if item.startswith("__") and item.endswith("__"):
1✔
2009
            raise AttributeError(
1✔
2010
                "'{cls}' object has no attribute "
2011
                "'{attribute_name}'".format(cls=self.__class__.__name__, attribute_name=item)
1✔
2012
            )
2013

2014
        child = self._children.get(item)
1✔
2015
        if child is None:
1✔
2016
            child = ConfigurationOption((item,), self)
1✔
2017
            self._children[item] = child
1✔
2018
        return child
1✔
2019

2020
    def __getitem__(self, item):
2021
        child = self._children.get(item)
×
2022
        if child is None:
×
2023
            child = ConfigurationOption(item, self)
×
2024
            self._children[item] = child
×
2025
        return child
×
2026

2027
    def get_name(self):
1✔
2028
        """Return name."""
2029
        return self._name
1✔
2030

2031
    def set_name(self, name):
1✔
2032
        """Set name."""
2033
        self._name = name
1✔
2034
        return self
1✔
2035

2036
    def get_default(self):
1✔
2037
        """Return default."""
2038
        return self.provides
1✔
2039

2040
    def set_default(self, default):
1✔
2041
        """Set default."""
2042
        if not default:
1✔
2043
            return self
1✔
2044

2045
        assert isinstance(default, dict), default
1✔
2046
        self.set_provides(default.copy())
1✔
2047
        return self
1✔
2048

2049
    def get_strict(self):
1✔
2050
        """Return strict flag."""
2051
        return self.__strict
1✔
2052

2053
    def set_strict(self, strict):
1✔
2054
        """Set strict flag."""
2055
        self.__strict = strict
1✔
2056
        return self
1✔
2057

2058
    def get_children(self):
1✔
2059
        """Return children options."""
2060
        return self._children
1✔
2061

2062
    def set_children(self, children):
1✔
2063
        """Set children options."""
2064
        self._children = children
1✔
2065
        return self
1✔
2066

2067
    def get_ini_files(self):
1✔
2068
        """Return list of INI files."""
2069
        return list(self._ini_files)
1✔
2070

2071
    def set_ini_files(self, files):
1✔
2072
        """Set list of INI files."""
2073
        self._ini_files = list(files)
1✔
2074
        return self
1✔
2075

2076
    def get_yaml_files(self):
1✔
2077
        """Return list of YAML files."""
2078
        return list(self._yaml_files)
1✔
2079

2080
    def set_yaml_files(self, files):
1✔
2081
        """Set list of YAML files."""
2082
        self._yaml_files = list(files)
1✔
2083
        return self
1✔
2084

2085
    def get_json_files(self):
1✔
2086
        """Return list of JSON files."""
2087
        return list(self._json_files)
1✔
2088

2089
    def set_json_files(self, files):
1✔
2090
        """Set list of JSON files."""
2091
        self._json_files = list(files)
1✔
2092
        return self
1✔
2093

2094
    def get_pydantic_settings(self):
1✔
2095
        """Return list of Pydantic settings."""
2096
        return list(self._pydantic_settings)
1✔
2097

2098
    def set_pydantic_settings(self, settings):
1✔
2099
        """Set list of Pydantic settings."""
2100
        self._pydantic_settings = list(settings)
1✔
2101
        return self
1✔
2102

2103
    def load(self, required=UNDEFINED, envs_required=UNDEFINED):
1✔
2104
        """Load configuration.
2105

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

2109
        .. code-block:: python
2110

2111
           config = providers.Configuration(yaml_files=[file1, file2])
2112
           config.load()
2113

2114
        :param required: When required is True, raise an exception if file does not exist.
2115
        :type required: bool
2116

2117
        :param envs_required: When True, raises an error on undefined environment variable.
2118
        :type envs_required: bool
2119
        """
2120
        for file in self.get_ini_files():
1✔
2121
            self.from_ini(file, required=required, envs_required=envs_required)
1✔
2122

2123
        for file in self.get_yaml_files():
1✔
2124
            self.from_yaml(file, required=required, envs_required=envs_required)
1✔
2125

2126
        for file in self.get_json_files():
1✔
2127
            self.from_json(file, required=required, envs_required=envs_required)
1✔
2128

2129
        for settings in self.get_pydantic_settings():
1✔
2130
            self.from_pydantic(settings, required=required)
1✔
2131

2132
    def get(self, selector, required=False):
1✔
2133
        """Return configuration option.
2134

2135
        :param selector: Selector string, e.g. "option1.option2"
2136
        :type selector: str
2137

2138
        :param required: Required flag, raise error if required option is missing
2139
        :type required: bool
2140

2141
        :return: Option value.
2142
        :rtype: Any
2143
        """
2144
        value = self.__call__()
1✔
2145

2146
        if value is None:
1✔
2147
            if self._is_strict_mode_enabled() or required:
1✔
2148
                raise Error("Undefined configuration option \"{0}.{1}\"".format(self._name, selector))
1✔
2149
            return None
1✔
2150

2151
        keys = selector.split(".")
1✔
2152
        while len(keys) > 0:
1✔
2153
            key = keys.pop(0)
1✔
2154
            value = value.get(key, UNDEFINED)
1✔
2155

2156
            if value is UNDEFINED:
1✔
2157
                if self._is_strict_mode_enabled() or required:
1✔
2158
                    raise Error("Undefined configuration option \"{0}.{1}\"".format(self._name, selector))
1✔
2159
                return None
1✔
2160

2161
        return value
1✔
2162

2163
    def set(self, selector, value):
1✔
2164
        """Override configuration option.
2165

2166
        :param selector: Selector string, e.g. "option1.option2"
2167
        :type selector: str
2168

2169
        :param value: Overriding value
2170
        :type value: Any
2171

2172
        :return: Overriding context.
2173
        :rtype: :py:class:`OverridingContext`
2174
        """
2175
        original_value = current_value = deepcopy(self.__call__())
1✔
2176

2177
        keys = selector.split(".")
1✔
2178
        while len(keys) > 0:
1✔
2179
            key = keys.pop(0)
1✔
2180
            if len(keys) == 0:
1✔
2181
                current_value[key] = value
1✔
2182
                break
1✔
2183
            temp_value = current_value.get(key, {})
1✔
2184
            current_value[key] = temp_value
1✔
2185
            current_value = temp_value
1✔
2186

2187
        return self.override(original_value)
1✔
2188

2189
    def override(self, provider):
1✔
2190
        """Override provider with another provider.
2191

2192
        :param provider: Overriding provider.
2193
        :type provider: :py:class:`Provider`
2194

2195
        :raise: :py:exc:`dependency_injector.errors.Error`
2196

2197
        :return: Overriding context.
2198
        :rtype: :py:class:`OverridingContext`
2199
        """
2200
        context = super().override(provider)
1✔
2201
        self.reset_cache()
1✔
2202
        return context
1✔
2203

2204
    def reset_last_overriding(self):
1✔
2205
        """Reset last overriding provider.
2206

2207
        :raise: :py:exc:`dependency_injector.errors.Error` if provider is not
2208
                overridden.
2209

2210
        :rtype: None
2211
        """
2212
        super().reset_last_overriding()
1✔
2213
        self.reset_cache()
1✔
2214

2215
    def reset_override(self):
1✔
2216
        """Reset all overriding providers.
2217

2218
        :rtype: None
2219
        """
2220
        super().reset_override()
1✔
2221
        self.reset_cache()
1✔
2222

2223
    def reset_cache(self):
1✔
2224
        """Reset children providers cache.
2225

2226
        :rtype: None
2227
        """
2228
        for provider in self._children.values():
1✔
2229
            provider.reset_cache()
1✔
2230

2231
        for provider in self.overrides:
1✔
2232
            if isinstance(provider, (Configuration, ConfigurationOption)):
1✔
2233
                provider.reset_cache()
1✔
2234

2235
    def update(self, value):
1✔
2236
        """Set configuration options.
2237

2238
        .. deprecated:: 3.11
2239

2240
            Use :py:meth:`Configuration.override` instead.
2241

2242
        :param value: Value of configuration option.
2243
        :type value: object | dict
2244

2245
        :rtype: None
2246
        """
2247
        self.override(value)
1✔
2248

2249
    def from_ini(self, filepath, required=UNDEFINED, envs_required=UNDEFINED):
1✔
2250
        """Load configuration from the ini file.
2251

2252
        Loaded configuration is merged recursively over existing configuration.
2253

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

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

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

2263
        :rtype: None
2264
        """
2265
        try:
1✔
2266
            parser = _parse_ini_file(
1✔
2267
                filepath,
1✔
2268
                envs_required=envs_required if envs_required is not UNDEFINED else self._is_strict_mode_enabled(),
1✔
2269
            )
2270
        except IOError as exception:
1✔
2271
            if required is not False \
1✔
2272
                    and (self._is_strict_mode_enabled() or required is True) \
1✔
2273
                    and exception.errno in (errno.ENOENT, errno.EISDIR):
1✔
2274
                exception.strerror = "Unable to load configuration file {0}".format(exception.strerror)
1✔
2275
                raise
1✔
2276
            return
1✔
2277

2278
        config = {}
1✔
2279
        for section in parser.sections():
1✔
2280
            config[section] = dict(parser.items(section))
1✔
2281

2282
        current_config = self.__call__()
1✔
2283
        if not current_config:
1✔
2284
            current_config = {}
1✔
2285
        self.override(merge_dicts(current_config, config))
1✔
2286

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

2290
        Loaded configuration is merged recursively over existing configuration.
2291

2292
        :param filepath: Path to the configuration file.
2293
        :type filepath: str
2294

2295
        :param required: When required is True, raise an exception if file does not exist.
2296
        :type required: bool
2297

2298
        :param loader: YAML loader, :py:class:`YamlLoader` is used if not specified.
2299
        :type loader: ``yaml.Loader``
2300

2301
        :param envs_required: When True, raises an error on undefined environment variable.
2302
        :type envs_required: bool
2303

2304
        :rtype: None
2305
        """
2306
        if yaml is None:
1✔
2307
            raise Error(
1✔
2308
                "Unable to load yaml configuration - PyYAML is not installed. "
2309
                "Install PyYAML or install Dependency Injector with yaml extras: "
2310
                "\"pip install dependency-injector[yaml]\""
2311
            )
2312

2313
        if loader is None:
1✔
2314
            loader = YamlLoader
1✔
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
        config_content = _resolve_config_env_markers(
1✔
2328
            config_content,
1✔
2329
            envs_required=envs_required if envs_required is not UNDEFINED else self._is_strict_mode_enabled(),
1✔
2330
        )
2331
        config = yaml.load(config_content, loader)
1✔
2332

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

2338
    def from_json(self, filepath, required=UNDEFINED, envs_required=UNDEFINED):
1✔
2339
        """Load configuration from a json file.
2340

2341
        Loaded configuration is merged recursively over the existing configuration.
2342

2343
        :param filepath: Path to a configuration file.
2344
        :type filepath: str
2345

2346
        :param required: When required is True, raise an exception if file does not exist.
2347
        :type required: bool
2348

2349
        :param envs_required: When True, raises an exception on undefined environment variable.
2350
        :type envs_required: bool
2351

2352
        :rtype: None
2353
        """
2354
        try:
1✔
2355
            with open(filepath) as opened_file:
1✔
2356
                config_content = opened_file.read()
1✔
2357
        except IOError as exception:
1✔
2358
            if required is not False \
1✔
2359
                    and (self._is_strict_mode_enabled() or required is True) \
1✔
2360
                    and exception.errno in (errno.ENOENT, errno.EISDIR):
1✔
2361
                exception.strerror = "Unable to load configuration file {0}".format(exception.strerror)
1✔
2362
                raise
1✔
2363
            return
1✔
2364

2365
        config_content = _resolve_config_env_markers(
1✔
2366
            config_content,
1✔
2367
            envs_required=envs_required if envs_required is not UNDEFINED else self._is_strict_mode_enabled(),
1✔
2368
        )
2369
        config = json.loads(config_content)
1✔
2370

2371
        current_config = self.__call__()
1✔
2372
        if not current_config:
1✔
2373
            current_config = {}
1✔
2374
        self.override(merge_dicts(current_config, config))
1✔
2375

2376
    def from_pydantic(self, settings, required=UNDEFINED, **kwargs):
1✔
2377
        """Load configuration from pydantic settings.
2378

2379
        Loaded configuration is merged recursively over existing configuration.
2380

2381
        :param settings: Pydantic settings instances.
2382
        :type settings: :py:class:`pydantic.BaseSettings` (pydantic v1) or
2383
            :py:class:`pydantic_settings.BaseSettings` (pydantic v2 and onwards)
2384

2385
        :param required: When required is True, raise an exception if settings dict is empty.
2386
        :type required: bool
2387

2388
        :param kwargs: Keyword arguments forwarded to ``pydantic.BaseSettings.dict()`` call.
2389
        :type kwargs: Dict[Any, Any]
2390

2391
        :rtype: None
2392
        """
2393

2394
        self.from_dict(pydantic_settings_to_dict(settings, kwargs), required=required)
1✔
2395

2396
    def from_dict(self, options, required=UNDEFINED):
1✔
2397
        """Load configuration from the dictionary.
2398

2399
        Loaded configuration is merged recursively over existing configuration.
2400

2401
        :param options: Configuration options.
2402
        :type options: dict
2403

2404
        :param required: When required is True, raise an exception if dictionary is empty.
2405
        :type required: bool
2406

2407
        :rtype: None
2408
        """
2409
        if required is not False \
1✔
2410
                and (self._is_strict_mode_enabled() or required is True) \
1✔
2411
                and not options:
1✔
2412
            raise ValueError("Can not use empty dictionary")
1✔
2413

2414
        current_config = self.__call__()
1✔
2415
        if not current_config:
1✔
2416
            current_config = {}
1✔
2417
        self.override(merge_dicts(current_config, options))
1✔
2418

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

2422
        :param name: Name of the environment variable.
2423
        :type name: str
2424

2425
        :param default: Default value that is used if environment variable does not exist.
2426
        :type default: object
2427

2428
        :param required: When required is True, raise an exception if environment variable is undefined.
2429
        :type required: bool
2430

2431
        :param as_: Callable used for type casting (int, float, etc).
2432
        :type as_: object
2433

2434
        :rtype: None
2435
        """
2436
        value = os.environ.get(name, default)
1✔
2437

2438
        if value is UNDEFINED:
1✔
2439
            if required is not False \
1✔
2440
                    and (self._is_strict_mode_enabled() or required is True):
1✔
2441
                raise ValueError("Environment variable \"{0}\" is undefined".format(name))
1✔
2442
            value = None
1✔
2443

2444
        if as_ is not UNDEFINED:
1✔
2445
            value = as_(value)
1✔
2446

2447
        self.override(value)
1✔
2448

2449
    def from_value(self, value):
1✔
2450
        """Load configuration value.
2451

2452
        :param value: Configuration value
2453
        :type value: object
2454

2455
        :rtype: None
2456
        """
2457
        self.override(value)
1✔
2458

2459
    @property
2460
    def related(self):
2461
        """Return related providers generator."""
2462
        yield from self._children.values()
1✔
2463
        yield from super().related
1✔
2464

2465
    def _is_strict_mode_enabled(self):
1✔
2466
        return self.__strict
1✔
2467

2468

2469
cdef class Factory(Provider):
2470
    r"""Factory provider creates new instance on every call.
2471

2472
    :py:class:`Factory` supports positional & keyword argument injections,
2473
    as well as attribute injections.
2474

2475
    Positional and keyword argument injections could be defined like this:
2476

2477
    .. code-block:: python
2478

2479
        factory = Factory(SomeClass,
2480
                          "positional_arg1", "positional_arg2",
2481
                          keyword_argument1=3, keyword_argument=4)
2482

2483
        # or
2484

2485
        factory = Factory(SomeClass) \
2486
            .add_args("positional_arg1", "positional_arg2") \
2487
            .add_kwargs(keyword_argument1=3, keyword_argument=4)
2488

2489
        # or
2490

2491
        factory = Factory(SomeClass)
2492
        factory.add_args("positional_arg1", "positional_arg2")
2493
        factory.add_kwargs(keyword_argument1=3, keyword_argument=4)
2494

2495

2496
    Attribute injections are defined by using
2497
    :py:meth:`Factory.add_attributes`:
2498

2499
    .. code-block:: python
2500

2501
        factory = Factory(SomeClass) \
2502
            .add_attributes(attribute1=1, attribute2=2)
2503

2504
    Retrieving of provided instance can be performed via calling
2505
    :py:class:`Factory` object:
2506

2507
    .. code-block:: python
2508

2509
        factory = Factory(SomeClass)
2510
        some_object = factory()
2511

2512
    .. py:attribute:: provided_type
2513

2514
        If provided type is defined, provider checks that providing class is
2515
        its subclass.
2516

2517
        :type: type | None
2518
    """
2519

2520
    provided_type = None
1✔
2521

2522
    def __init__(self, provides=None, *args, **kwargs):
2523
        """Initialize provider."""
2524
        self._instantiator = Callable()
1✔
2525
        self.set_provides(provides)
1✔
2526
        self.set_args(*args)
1✔
2527
        self.set_kwargs(**kwargs)
1✔
2528

2529
        self._attributes = tuple()
1✔
2530
        self._attributes_len = 0
1✔
2531

2532
        super(Factory, self).__init__()
1✔
2533

2534
    def __deepcopy__(self, memo):
1✔
2535
        """Create and return full copy of provider."""
2536
        copied = memo.get(id(self))
1✔
2537
        if copied is not None:
1✔
2538
            return copied
×
2539

2540
        copied = _memorized_duplicate(self, memo)
1✔
2541
        copied.set_provides(_copy_if_provider(self.provides, memo))
1✔
2542
        copied.set_args(*deepcopy(self.args, memo))
1✔
2543
        copied.set_kwargs(**deepcopy(self.kwargs, memo))
1✔
2544
        copied.set_attributes(**deepcopy(self.attributes, memo))
1✔
2545
        self._copy_overridings(copied, memo)
1✔
2546
        return copied
1✔
2547

2548
    def __str__(self):
2549
        """Return string representation of provider.
2550

2551
        :rtype: str
2552
        """
2553
        return represent_provider(provider=self,
1✔
2554
                                  provides=self._instantiator.provides)
1✔
2555

2556
    @property
2557
    def cls(self):
2558
        """Return provided type."""
2559
        return self.provides
1✔
2560

2561
    @property
2562
    def provides(self):
2563
        """Return provider provides."""
2564
        return self._instantiator.provides
1✔
2565

2566
    def set_provides(self, provides):
1✔
2567
        """Set provider provides."""
2568
        provides = _resolve_string_import(provides)
1✔
2569
        if (provides
1✔
2570
                and self.__class__.provided_type and
1✔
2571
                not issubclass(provides, self.__class__.provided_type)):
1✔
2572
            raise Error(
1✔
2573
                "{0} can provide only {1} instances".format(
1✔
2574
                    _class_qualname(self),
1✔
2575
                    self.__class__.provided_type,
1✔
2576
                ),
2577
            )
2578
        self._instantiator.set_provides(provides)
1✔
2579
        return self
1✔
2580

2581
    @property
2582
    def args(self):
2583
        """Return positional argument injections."""
2584
        return self._instantiator.args
1✔
2585

2586
    def add_args(self, *args):
1✔
2587
        """Add __init__ positional argument injections.
2588

2589
        :return: Reference ``self``
2590
        """
2591
        self._instantiator.add_args(*args)
1✔
2592
        return self
1✔
2593

2594
    def set_args(self, *args):
1✔
2595
        """Set __init__ positional argument injections.
2596

2597
        Existing __init__ positional argument injections are dropped.
2598

2599
        :return: Reference ``self``
2600
        """
2601
        self._instantiator.set_args(*args)
1✔
2602
        return self
1✔
2603

2604
    def clear_args(self):
1✔
2605
        """Drop __init__ positional argument injections.
2606

2607
        :return: Reference ``self``
2608
        """
2609
        self._instantiator.clear_args()
1✔
2610
        return self
1✔
2611

2612
    @property
2613
    def kwargs(self):
2614
        """Return keyword argument injections."""
2615
        return self._instantiator.kwargs
1✔
2616

2617
    def add_kwargs(self, **kwargs):
1✔
2618
        """Add __init__ keyword argument injections.
2619

2620
        :return: Reference ``self``
2621
        """
2622
        self._instantiator.add_kwargs(**kwargs)
1✔
2623
        return self
1✔
2624

2625
    def set_kwargs(self, **kwargs):
1✔
2626
        """Set __init__ keyword argument injections.
2627

2628
        Existing __init__ keyword argument injections are dropped.
2629

2630
        :return: Reference ``self``
2631
        """
2632
        self._instantiator.set_kwargs(**kwargs)
1✔
2633
        return self
1✔
2634

2635
    def clear_kwargs(self):
1✔
2636
        """Drop __init__ keyword argument injections.
2637

2638
        :return: Reference ``self``
2639
        """
2640
        self._instantiator.clear_kwargs()
1✔
2641
        return self
1✔
2642

2643
    @property
2644
    def attributes(self):
2645
        """Return attribute injections."""
2646
        cdef int index
2647
        cdef NamedInjection attribute
2648
        cdef dict attributes
2649

2650
        attributes = dict()
1✔
2651
        for index in range(self._attributes_len):
1✔
2652
            attribute = self._attributes[index]
1✔
2653
            attributes[attribute._name] = attribute._value
1✔
2654
        return attributes
1✔
2655

2656
    def add_attributes(self, **kwargs):
1✔
2657
        """Add attribute injections.
2658

2659
        :return: Reference ``self``
2660
        """
2661
        self._attributes += parse_named_injections(kwargs)
1✔
2662
        self._attributes_len = len(self._attributes)
1✔
2663
        return self
1✔
2664

2665
    def set_attributes(self, **kwargs):
1✔
2666
        """Set attribute injections.
2667

2668
        Existing attribute injections are dropped.
2669

2670
        :return: Reference ``self``
2671
        """
2672
        self._attributes = parse_named_injections(kwargs)
1✔
2673
        self._attributes_len = len(self._attributes)
1✔
2674
        return self
1✔
2675

2676
    def clear_attributes(self):
1✔
2677
        """Drop attribute injections.
2678

2679
        :return: Reference ``self``
2680
        """
2681
        self._attributes = tuple()
1✔
2682
        self._attributes_len = len(self._attributes)
1✔
2683
        return self
1✔
2684

2685
    @property
2686
    def related(self):
2687
        """Return related providers generator."""
2688
        yield from filter(is_provider, [self.provides])
1✔
2689
        yield from filter(is_provider, self.args)
1✔
2690
        yield from filter(is_provider, self.kwargs.values())
1✔
2691
        yield from filter(is_provider, self.attributes.values())
1✔
2692
        yield from super().related
1✔
2693

2694
    cpdef object _provide(self, tuple args, dict kwargs):
1✔
2695
        """Return new instance."""
2696
        return __factory_call(self, args, kwargs)
1✔
2697

2698

2699
cdef class DelegatedFactory(Factory):
2700
    """Factory that is injected "as is".
2701

2702
    .. py:attribute:: provided_type
2703

2704
        If provided type is defined, provider checks that providing class is
2705
        its subclass.
2706

2707
        :type: type | None
2708

2709
    .. py:attribute:: cls
2710
       :noindex:
2711

2712
        Class that provides object.
2713
        Alias for :py:attr:`provides`.
2714

2715
        :type: type
2716
    """
2717

2718
    __IS_DELEGATED__ = True
1✔
2719

2720

2721
cdef class AbstractFactory(Factory):
2722
    """Abstract factory provider.
2723

2724
    :py:class:`AbstractFactory` is a :py:class:`Factory` provider that must
2725
    be explicitly overridden before calling.
2726

2727
    Overriding of :py:class:`AbstractFactory` is possible only by another
2728
    :py:class:`Factory` provider.
2729
    """
2730

2731
    def __call__(self, *args, **kwargs):
2732
        """Return provided object.
2733

2734
        Callable interface implementation.
2735
        """
2736
        if self._last_overriding is None:
1✔
2737
            raise Error("{0} must be overridden before calling".format(self))
1✔
2738
        return super().__call__(*args, **kwargs)
1✔
2739

2740
    def override(self, provider):
1✔
2741
        """Override provider with another provider.
2742

2743
        :param provider: Overriding provider.
2744
        :type provider: :py:class:`Provider`
2745

2746
        :raise: :py:exc:`dependency_injector.errors.Error`
2747

2748
        :return: Overriding context.
2749
        :rtype: :py:class:`OverridingContext`
2750
        """
2751
        if not isinstance(provider, Factory):
1✔
2752
            raise Error("{0} must be overridden only by "
1✔
2753
                        "{1} providers".format(self, Factory))
1✔
2754
        return super(AbstractFactory, self).override(provider)
1✔
2755

2756
    cpdef object _provide(self, tuple args, dict kwargs):
1✔
2757
        """Return result of provided callable call."""
2758
        raise NotImplementedError("Abstract provider forward providing logic to overriding provider")
1✔
2759

2760

2761
cdef class FactoryDelegate(Delegate):
2762
    """Factory delegate injects delegating factory "as is".
2763

2764
    .. py:attribute:: provides
2765

2766
        Value that have to be provided.
2767

2768
        :type: object
2769
    """
2770

2771
    def __init__(self, factory):
2772
        """Initializer.
2773

2774
        :param factory: Value that have to be provided.
2775
        :type factory: object
2776
        """
2777
        if isinstance(factory, Factory) is False:
1✔
2778
            raise Error("{0} can wrap only {1} providers".format(self.__class__, Factory))
1✔
2779
        super(FactoryDelegate, self).__init__(factory)
1✔
2780

2781

2782
cdef class FactoryAggregate(Aggregate):
2783
    """Factory providers aggregate.
2784

2785
    :py:class:`FactoryAggregate` is an aggregate of :py:class:`Factory`
2786
    providers.
2787

2788
    :py:class:`FactoryAggregate` is a delegated provider, meaning that it is
2789
    injected "as is".
2790

2791
    All aggregated providers can be retrieved as a read-only
2792
    dictionary :py:attr:`FactoryAggregate.providers` or as an attribute of
2793
    :py:class:`FactoryAggregate`.
2794
    """
2795

2796
    @property
2797
    def factories(self):
2798
        """Return dictionary of factories, read-only.
2799

2800
        Alias for ``.providers()`` attribute.
2801
        """
2802
        return self.providers
1✔
2803

2804
    def set_factories(self, factory_dict=None, **factory_kwargs):
1✔
2805
        """Set factories.
2806

2807
        Alias for ``.set_providers()`` method.
2808
        """
2809
        return self.set_providers(factory_dict, **factory_kwargs)
1✔
2810

2811

2812
cdef class BaseSingleton(Provider):
2813
    """Base class of singleton providers."""
2814

2815
    provided_type = None
1✔
2816

2817
    def __init__(self, provides=None, *args, **kwargs):
2818
        """Initialize provider."""
2819
        self._instantiator = Factory()
1✔
2820
        self.set_provides(provides)
1✔
2821
        self.set_args(*args)
1✔
2822
        self.set_kwargs(**kwargs)
1✔
2823
        super(BaseSingleton, self).__init__()
1✔
2824

2825
    def __str__(self):
2826
        """Return string representation of provider.
2827

2828
        :rtype: str
2829
        """
2830
        return represent_provider(provider=self,
1✔
2831
                                  provides=self._instantiator.cls)
1✔
2832

2833
    def __deepcopy__(self, memo):
1✔
2834
        """Create and return full copy of provider."""
2835
        copied = memo.get(id(self))
1✔
2836
        if copied is not None:
1✔
2837
            return copied
×
2838

2839
        copied = _memorized_duplicate(self, memo)
1✔
2840
        copied.set_provides(_copy_if_provider(self.provides, memo))
1✔
2841
        copied.set_args(*deepcopy(self.args, memo))
1✔
2842
        copied.set_kwargs(**deepcopy(self.kwargs, memo))
1✔
2843
        copied.set_attributes(**deepcopy(self.attributes, memo))
1✔
2844
        self._copy_overridings(copied, memo)
1✔
2845
        return copied
1✔
2846

2847
    @property
2848
    def cls(self):
2849
        """Return provided type."""
2850
        return self.provides
1✔
2851

2852
    @property
2853
    def provides(self):
2854
        """Return provider provides."""
2855
        return self._instantiator.provides
1✔
2856

2857
    def set_provides(self, provides):
1✔
2858
        """Set provider provides."""
2859
        provides = _resolve_string_import(provides)
1✔
2860
        if (provides
1✔
2861
                and self.__class__.provided_type and
1✔
2862
                not issubclass(provides, self.__class__.provided_type)):
1✔
2863
            raise Error(
1✔
2864
                "{0} can provide only {1} instances".format(
1✔
2865
                    _class_qualname(self),
1✔
2866
                    self.__class__.provided_type,
1✔
2867
                ),
2868
            )
2869
        self._instantiator.set_provides(provides)
1✔
2870
        return self
1✔
2871

2872
    @property
2873
    def args(self):
2874
        """Return positional argument injections."""
2875
        return self._instantiator.args
1✔
2876

2877
    def add_args(self, *args):
1✔
2878
        """Add __init__ positional argument injections.
2879

2880
        :return: Reference ``self``
2881
        """
2882
        self._instantiator.add_args(*args)
1✔
2883
        return self
1✔
2884

2885
    def set_args(self, *args):
1✔
2886
        """Set __init__ positional argument injections.
2887

2888
        Existing __init__ positional argument injections are dropped.
2889

2890
        :return: Reference ``self``
2891
        """
2892
        self._instantiator.set_args(*args)
1✔
2893
        return self
1✔
2894

2895
    def clear_args(self):
1✔
2896
        """Drop __init__ positional argument injections.
2897

2898
        :return: Reference ``self``
2899
        """
2900
        self._instantiator.clear_args()
1✔
2901
        return self
1✔
2902

2903
    @property
2904
    def kwargs(self):
2905
        """Return keyword argument injections."""
2906
        return self._instantiator.kwargs
1✔
2907

2908
    def add_kwargs(self, **kwargs):
1✔
2909
        """Add __init__ keyword argument injections.
2910

2911
        :return: Reference ``self``
2912
        """
2913
        self._instantiator.add_kwargs(**kwargs)
1✔
2914
        return self
1✔
2915

2916
    def set_kwargs(self, **kwargs):
1✔
2917
        """Set __init__ keyword argument injections.
2918

2919
        Existing __init__ keyword argument injections are dropped.
2920

2921
        :return: Reference ``self``
2922
        """
2923
        self._instantiator.set_kwargs(**kwargs)
1✔
2924
        return self
1✔
2925

2926
    def clear_kwargs(self):
1✔
2927
        """Drop __init__ keyword argument injections.
2928

2929
        :return: Reference ``self``
2930
        """
2931
        self._instantiator.clear_kwargs()
1✔
2932
        return self
1✔
2933

2934
    @property
2935
    def attributes(self):
2936
        """Return attribute injections."""
2937
        return self._instantiator.attributes
1✔
2938

2939
    def add_attributes(self, **kwargs):
1✔
2940
        """Add attribute injections.
2941

2942
        :return: Reference ``self``
2943
        """
2944
        self._instantiator.add_attributes(**kwargs)
1✔
2945
        return self
1✔
2946

2947
    def set_attributes(self, **kwargs):
1✔
2948
        """Set attribute injections.
2949

2950
        Existing attribute injections are dropped.
2951

2952
        :return: Reference ``self``
2953
        """
2954
        self._instantiator.set_attributes(**kwargs)
1✔
2955
        return self
1✔
2956

2957
    def clear_attributes(self):
1✔
2958
        """Drop attribute injections.
2959

2960
        :return: Reference ``self``
2961
        """
2962
        self._instantiator.clear_attributes()
1✔
2963
        return self
1✔
2964

2965
    def reset(self):
1✔
2966
        """Reset cached instance, if any.
2967

2968
        :rtype: None
2969
        """
2970
        raise NotImplementedError()
×
2971

2972
    def full_reset(self):
1✔
2973
        """Reset cached instance in current and all underlying singletons, if any.
2974

2975
        :rtype: :py:class:`SingletonFullResetContext`
2976
        """
2977
        self.reset()
1✔
2978
        for provider in self.traverse(types=[BaseSingleton]):
1✔
2979
            provider.reset()
1✔
2980
        return SingletonFullResetContext(self)
1✔
2981

2982
    @property
2983
    def related(self):
2984
        """Return related providers generator."""
2985
        yield from filter(is_provider, [self._instantiator.provides])
1✔
2986
        yield from filter(is_provider, self.args)
1✔
2987
        yield from filter(is_provider, self.kwargs.values())
1✔
2988
        yield from filter(is_provider, self.attributes.values())
1✔
2989
        yield from super().related
1✔
2990

2991
    def _async_init_instance(self, future_result, result):
1✔
2992
        try:
1✔
2993
            instance = result.result()
1✔
2994
        except Exception as exception:
1✔
2995
            self._storage = None
1✔
2996
            future_result.set_exception(exception)
1✔
2997
        else:
2998
            self._storage = instance
1✔
2999
            future_result.set_result(instance)
1✔
3000

3001

3002
cdef class Singleton(BaseSingleton):
3003
    """Singleton provider returns same instance on every call.
3004

3005
    :py:class:`Singleton` provider creates instance once and returns it on
3006
    every call. :py:class:`Singleton` extends :py:class:`Factory`, so, please
3007
    follow :py:class:`Factory` documentation for getting familiar with
3008
    injections syntax.
3009

3010
    Retrieving of provided instance can be performed via calling
3011
    :py:class:`Singleton` object:
3012

3013
    .. code-block:: python
3014

3015
        singleton = Singleton(SomeClass)
3016
        some_object = singleton()
3017

3018
    .. py:attribute:: provided_type
3019

3020
        If provided type is defined, provider checks that providing class is
3021
        its subclass.
3022

3023
        :type: type | None
3024

3025
    .. py:attribute:: cls
3026
       :noindex:
3027

3028
        Class that provides object.
3029
        Alias for :py:attr:`provides`.
3030

3031
        :type: type
3032
    """
3033

3034
    def __init__(self, provides=None, *args, **kwargs):
3035
        """Initializer.
3036

3037
        :param provides: Provided type.
3038
        :type provides: type
3039
        """
3040
        self._storage = None
1✔
3041
        super(Singleton, self).__init__(provides, *args, **kwargs)
1✔
3042

3043
    def reset(self):
1✔
3044
        """Reset cached instance, if any.
3045

3046
        :rtype: None
3047
        """
3048
        if __is_future_or_coroutine(self._storage):
1✔
3049
            asyncio.ensure_future(self._storage).cancel()
×
3050
        self._storage = None
1✔
3051
        return SingletonResetContext(self)
1✔
3052

3053
    cpdef object _provide(self, tuple args, dict kwargs):
1✔
3054
        """Return single instance."""
3055
        if self._storage is None:
1✔
3056
            instance = __factory_call(self._instantiator, args, kwargs)
1✔
3057

3058
            if __is_future_or_coroutine(instance):
1✔
3059
                future_result = asyncio.Future()
1✔
3060
                instance = asyncio.ensure_future(instance)
1✔
3061
                instance.add_done_callback(functools.partial(self._async_init_instance, future_result))
1✔
3062
                self._storage = future_result
1✔
3063
                return future_result
1✔
3064

3065
            self._storage = instance
1✔
3066

3067
        return self._storage
1✔
3068

3069

3070
cdef class DelegatedSingleton(Singleton):
3071
    """Delegated singleton is injected "as is".
3072

3073
    .. py:attribute:: provided_type
3074

3075
        If provided type is defined, provider checks that providing class is
3076
        its subclass.
3077

3078
        :type: type | None
3079

3080
    .. py:attribute:: cls
3081
       :noindex:
3082

3083
        Class that provides object.
3084
        Alias for :py:attr:`provides`.
3085

3086
        :type: type
3087
    """
3088

3089
    __IS_DELEGATED__ = True
1✔
3090

3091

3092
cdef class ThreadSafeSingleton(BaseSingleton):
3093
    """Thread-safe singleton provider."""
3094

3095
    storage_lock = threading.RLock()
1✔
3096
    """Storage reentrant lock.
3097

3098
    :type: :py:class:`threading.RLock`
3099
    """
3100

3101
    def __init__(self, provides=None, *args, **kwargs):
3102
        """Initializer.
3103

3104
        :param provides: Provided type.
3105
        :type provides: type
3106
        """
3107
        self._storage = None
1✔
3108
        self._storage_lock = self.__class__.storage_lock
1✔
3109
        super(ThreadSafeSingleton, self).__init__(provides, *args, **kwargs)
1✔
3110

3111
    def reset(self):
1✔
3112
        """Reset cached instance, if any.
3113

3114
        :rtype: None
3115
        """
3116
        with self._storage_lock:
1✔
3117
            if __is_future_or_coroutine(self._storage):
1✔
3118
                asyncio.ensure_future(self._storage).cancel()
×
3119
            self._storage = None
1✔
3120
        return SingletonResetContext(self)
1✔
3121

3122
    cpdef object _provide(self, tuple args, dict kwargs):
1✔
3123
        """Return single instance."""
3124
        instance = self._storage
1✔
3125

3126
        if instance is None:
1✔
3127
            with self._storage_lock:
1✔
3128
                if self._storage is None:
1✔
3129
                    result = __factory_call(self._instantiator, args, kwargs)
1✔
3130
                    if __is_future_or_coroutine(result):
1✔
3131
                        future_result = asyncio.Future()
1✔
3132
                        result = asyncio.ensure_future(result)
1✔
3133
                        result.add_done_callback(functools.partial(self._async_init_instance, future_result))
1✔
3134
                        result = future_result
1✔
3135
                    self._storage = result
1✔
3136
                instance = self._storage
1✔
3137
        return instance
1✔
3138

3139

3140
cdef class DelegatedThreadSafeSingleton(ThreadSafeSingleton):
3141
    """Delegated thread-safe singleton is injected "as is".
3142

3143
    .. py:attribute:: provided_type
3144

3145
        If provided type is defined, provider checks that providing class is
3146
        its subclass.
3147

3148
        :type: type | None
3149

3150
    .. py:attribute:: cls
3151
       :noindex:
3152

3153
        Class that provides object.
3154
        Alias for :py:attr:`provides`.
3155

3156
        :type: type
3157
    """
3158

3159
    __IS_DELEGATED__ = True
1✔
3160

3161

3162
cdef class ThreadLocalSingleton(BaseSingleton):
3163
    """Thread-local singleton provides single objects in scope of thread.
3164

3165
    .. py:attribute:: provided_type
3166

3167
        If provided type is defined, provider checks that providing class is
3168
        its subclass.
3169

3170
        :type: type | None
3171

3172
    .. py:attribute:: cls
3173
       :noindex:
3174

3175
        Class that provides object.
3176
        Alias for :py:attr:`provides`.
3177

3178
        :type: type
3179
    """
3180

3181
    def __init__(self, provides=None, *args, **kwargs):
3182
        """Initializer.
3183

3184
        :param provides: Provided type.
3185
        :type provides: type
3186
        """
3187
        self._storage = threading.local()
1✔
3188
        super(ThreadLocalSingleton, self).__init__(provides, *args, **kwargs)
1✔
3189

3190
    def reset(self):
1✔
3191
        """Reset cached instance, if any.
3192

3193
        :rtype: None
3194
        """
3195
        try:
1✔
3196
            instance = self._storage.instance
1✔
3197
        except AttributeError:
1✔
3198
            return SingletonResetContext(self)
1✔
3199

3200
        if __is_future_or_coroutine(instance):
1✔
3201
            asyncio.ensure_future(instance).cancel()
×
3202

3203
        del self._storage.instance
1✔
3204

3205
        return SingletonResetContext(self)
1✔
3206

3207
    cpdef object _provide(self, tuple args, dict kwargs):
1✔
3208
        """Return single instance."""
3209
        cdef object instance
3210

3211
        try:
1✔
3212
            instance = self._storage.instance
1✔
3213
        except AttributeError:
1✔
3214
            instance = __factory_call(self._instantiator, args, kwargs)
1✔
3215

3216
            if __is_future_or_coroutine(instance):
1✔
3217
                future_result = asyncio.Future()
1✔
3218
                instance = asyncio.ensure_future(instance)
1✔
3219
                instance.add_done_callback(functools.partial(self._async_init_instance, future_result))
1✔
3220
                self._storage.instance = future_result
1✔
3221
                return future_result
1✔
3222

3223
            self._storage.instance = instance
1✔
3224
        finally:
3225
            return instance
1✔
3226

3227
    def _async_init_instance(self, future_result, result):
1✔
3228
        try:
1✔
3229
            instance = result.result()
1✔
3230
        except Exception as exception:
1✔
3231
            del self._storage.instance
1✔
3232
            future_result.set_exception(exception)
1✔
3233
        else:
3234
            self._storage.instance = instance
1✔
3235
            future_result.set_result(instance)
1✔
3236

3237

3238
cdef class ContextLocalSingleton(BaseSingleton):
3239
    """Context-local singleton provides single objects in scope of a context.
3240

3241
    .. py:attribute:: provided_type
3242

3243
        If provided type is defined, provider checks that providing class is
3244
        its subclass.
3245

3246
        :type: type | None
3247

3248
    .. py:attribute:: cls
3249
       :noindex:
3250

3251
        Class that provides object.
3252
        Alias for :py:attr:`provides`.
3253

3254
        :type: type
3255
    """
3256
    _none = object()
1✔
3257

3258
    def __init__(self, provides=None, *args, **kwargs):
3259
        """Initializer.
3260

3261
        :param provides: Provided type.
3262
        :type provides: type
3263
        """
3264
        if not contextvars:
1✔
3265
            raise RuntimeError(
×
3266
                "Contextvars library not found. This provider "
3267
                "requires Python 3.7 or a backport of contextvars. "
3268
                "To install a backport run \"pip install contextvars\"."
3269
            )
3270

3271
        super(ContextLocalSingleton, self).__init__(provides, *args, **kwargs)
1✔
3272
        self._storage = contextvars.ContextVar("_storage", default=self._none)
1✔
3273

3274
    def reset(self):
1✔
3275
        """Reset cached instance, if any.
3276

3277
        :rtype: None
3278
        """
3279
        instance = self._storage.get()
1✔
3280
        if instance is self._none:
1✔
3281
            return SingletonResetContext(self)
1✔
3282

3283
        if __is_future_or_coroutine(instance):
1✔
3284
            asyncio.ensure_future(instance).cancel()
×
3285

3286
        self._storage.set(self._none)
1✔
3287

3288
        return SingletonResetContext(self)
1✔
3289

3290
    cpdef object _provide(self, tuple args, dict kwargs):
1✔
3291
        """Return single instance."""
3292
        cdef object instance
3293

3294
        instance = self._storage.get()
1✔
3295

3296
        if instance is self._none:
1✔
3297
            instance = __factory_call(self._instantiator, args, kwargs)
1✔
3298

3299
            if __is_future_or_coroutine(instance):
1✔
3300
                future_result = asyncio.Future()
×
3301
                instance = asyncio.ensure_future(instance)
×
3302
                instance.add_done_callback(functools.partial(self._async_init_instance, future_result))
×
3303
                self._storage.set(future_result)
×
3304
                return future_result
×
3305

3306
            self._storage.set(instance)
1✔
3307

3308
        return instance
1✔
3309

3310
    def _async_init_instance(self, future_result, result):
1✔
3311
        try:
×
3312
            instance = result.result()
×
3313
        except Exception as exception:
×
3314
            self._storage.set(self._none)
×
3315
            future_result.set_exception(exception)
×
3316
        else:
3317
            self._storage.set(instance)
×
3318
            future_result.set_result(instance)
×
3319

3320

3321
cdef class DelegatedThreadLocalSingleton(ThreadLocalSingleton):
3322
    """Delegated thread-local singleton is injected "as is".
3323

3324
    .. py:attribute:: provided_type
3325

3326
        If provided type is defined, provider checks that providing class is
3327
        its subclass.
3328

3329
        :type: type | None
3330

3331
    .. py:attribute:: cls
3332
       :noindex:
3333

3334
        Class that provides object.
3335
        Alias for :py:attr:`provides`.
3336

3337
        :type: type
3338
    """
3339

3340
    __IS_DELEGATED__ = True
1✔
3341

3342

3343
cdef class AbstractSingleton(BaseSingleton):
3344
    """Abstract singleton provider.
3345

3346
    :py:class:`AbstractSingleton` is a :py:class:`Singleton` provider that must
3347
    be explicitly overridden before calling.
3348

3349
    Overriding of :py:class:`AbstractSingleton` is possible only by another
3350
    :py:class:`BaseSingleton` provider.
3351
    """
3352

3353
    def __call__(self, *args, **kwargs):
3354
        """Return provided object.
3355

3356
        Callable interface implementation.
3357
        """
3358
        if self._last_overriding is None:
1✔
3359
            raise Error("{0} must be overridden before calling".format(self))
1✔
3360
        return super().__call__(*args, **kwargs)
1✔
3361

3362
    def override(self, provider):
1✔
3363
        """Override provider with another provider.
3364

3365
        :param provider: Overriding provider.
3366
        :type provider: :py:class:`Provider`
3367

3368
        :raise: :py:exc:`dependency_injector.errors.Error`
3369

3370
        :return: Overriding context.
3371
        :rtype: :py:class:`OverridingContext`
3372
        """
3373
        if not isinstance(provider, BaseSingleton):
1✔
3374
            raise Error("{0} must be overridden only by "
1✔
3375
                        "{1} providers".format(self, BaseSingleton))
1✔
3376
        return super(AbstractSingleton, self).override(provider)
1✔
3377

3378
    def reset(self):
1✔
3379
        """Reset cached instance, if any.
3380

3381
        :rtype: None
3382
        """
3383
        if self._last_overriding is None:
1✔
3384
            raise Error("{0} must be overridden before calling".format(self))
1✔
3385
        return self._last_overriding.reset()
1✔
3386

3387

3388
cdef class SingletonDelegate(Delegate):
3389
    """Singleton delegate injects delegating singleton "as is".
3390

3391
    .. py:attribute:: provides
3392

3393
        Value that have to be provided.
3394

3395
        :type: object
3396
    """
3397

3398
    def __init__(self, singleton):
3399
        """Initializer.
3400

3401
        :param singleton: Value that have to be provided.
3402
        :type singleton: py:class:`BaseSingleton`
3403
        """
3404
        if isinstance(singleton, BaseSingleton) is False:
1✔
3405
            raise Error("{0} can wrap only {1} providers".format(
1✔
3406
                self.__class__, BaseSingleton))
1✔
3407
        super(SingletonDelegate, self).__init__(singleton)
1✔
3408

3409

3410
cdef class List(Provider):
3411
    """List provider provides a list of values.
3412

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

3416
    Keyword argument injections are not supported.
3417

3418
    .. code-block:: python
3419

3420
        dispatcher_factory = Factory(
3421
            Dispatcher,
3422
            modules=List(
3423
                Factory(ModuleA, dependency_a),
3424
                Factory(ModuleB, dependency_b),
3425
            ),
3426
        )
3427

3428
        dispatcher = dispatcher_factory()
3429

3430
        # is equivalent to:
3431

3432
        dispatcher = Dispatcher(
3433
            modules=[
3434
                ModuleA(dependency_a),
3435
                ModuleB(dependency_b),
3436
            ],
3437
        )
3438
    """
3439

3440
    def __init__(self, *args):
3441
        """Initializer."""
3442
        self._args = tuple()
1✔
3443
        self._args_len = 0
1✔
3444
        self.set_args(*args)
1✔
3445
        super(List, self).__init__()
1✔
3446

3447
    def __deepcopy__(self, memo):
1✔
3448
        """Create and return full copy of provider."""
3449
        copied = memo.get(id(self))
1✔
3450
        if copied is not None:
1✔
3451
            return copied
×
3452

3453
        copied = _memorized_duplicate(self, memo)
1✔
3454
        copied.set_args(*deepcopy(self.args, memo))
1✔
3455
        self._copy_overridings(copied, memo)
1✔
3456
        return copied
1✔
3457

3458
    def __str__(self):
3459
        """Return string representation of provider.
3460

3461
        :rtype: str
3462
        """
3463
        return represent_provider(provider=self, provides=list(self.args))
1✔
3464

3465
    @property
3466
    def args(self):
3467
        """Return positional argument injections."""
3468
        cdef int index
3469
        cdef PositionalInjection arg
3470
        cdef list args
3471

3472
        args = list()
1✔
3473
        for index in range(self._args_len):
1✔
3474
            arg = self._args[index]
1✔
3475
            args.append(arg._value)
1✔
3476
        return tuple(args)
1✔
3477

3478
    def add_args(self, *args):
1✔
3479
        """Add positional argument injections.
3480

3481
        :return: Reference ``self``
3482
        """
3483
        self._args += parse_positional_injections(args)
1✔
3484
        self._args_len = len(self._args)
1✔
3485
        return self
1✔
3486

3487
    def set_args(self, *args):
1✔
3488
        """Set positional argument injections.
3489

3490
        Existing positional argument injections are dropped.
3491

3492
        :return: Reference ``self``
3493
        """
3494
        self._args = parse_positional_injections(args)
1✔
3495
        self._args_len = len(self._args)
1✔
3496
        return self
1✔
3497

3498
    def clear_args(self):
1✔
3499
        """Drop positional argument injections.
3500

3501
        :return: Reference ``self``
3502
        """
3503
        self._args = tuple()
1✔
3504
        self._args_len = len(self._args)
1✔
3505
        return self
1✔
3506

3507
    @property
3508
    def related(self):
3509
        """Return related providers generator."""
3510
        yield from filter(is_provider, self.args)
1✔
3511
        yield from super().related
1✔
3512

3513
    cpdef object _provide(self, tuple args, dict kwargs):
1✔
3514
        """Return result of provided callable call."""
3515
        return __provide_positional_args(args, self._args, self._args_len, self._async_mode)
1✔
3516

3517

3518
cdef class Dict(Provider):
3519
    """Dict provider provides a dictionary of values.
3520

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

3524
    Positional argument injections are not supported.
3525

3526
    .. code-block:: python
3527

3528
        dispatcher_factory = Factory(
3529
            Dispatcher,
3530
            modules=Dict(
3531
                module1=Factory(ModuleA, dependency_a),
3532
                module2=Factory(ModuleB, dependency_b),
3533
            ),
3534
        )
3535

3536
        dispatcher = dispatcher_factory()
3537

3538
        # is equivalent to:
3539

3540
        dispatcher = Dispatcher(
3541
            modules={
3542
                "module1": ModuleA(dependency_a),
3543
                "module2": ModuleB(dependency_b),
3544
            },
3545
        )
3546
    """
3547

3548
    def __init__(self, dict_=None, **kwargs):
3549
        """Initializer."""
3550
        self._kwargs = tuple()
1✔
3551
        self._kwargs_len = 0
1✔
3552
        self.add_kwargs(dict_, **kwargs)
1✔
3553
        super(Dict, self).__init__()
1✔
3554

3555
    def __deepcopy__(self, memo):
1✔
3556
        """Create and return full copy of provider."""
3557
        copied = memo.get(id(self))
1✔
3558
        if copied is not None:
1✔
3559
            return copied
×
3560

3561
        copied = _memorized_duplicate(self, memo)
1✔
3562
        self._copy_kwargs(copied, memo)
1✔
3563
        self._copy_overridings(copied, memo)
1✔
3564
        return copied
1✔
3565

3566
    def __str__(self):
3567
        """Return string representation of provider.
3568

3569
        :rtype: str
3570
        """
3571
        return represent_provider(provider=self, provides=self.kwargs)
1✔
3572

3573
    @property
3574
    def kwargs(self):
3575
        """Return keyword argument injections."""
3576
        cdef int index
3577
        cdef NamedInjection kwarg
3578
        cdef dict kwargs
3579

3580
        kwargs = dict()
1✔
3581
        for index in range(self._kwargs_len):
1✔
3582
            kwarg = self._kwargs[index]
1✔
3583
            kwargs[kwarg._name] = kwarg._value
1✔
3584
        return kwargs
1✔
3585

3586
    def add_kwargs(self, dict_=None, **kwargs):
1✔
3587
        """Add keyword argument injections.
3588

3589
        :return: Reference ``self``
3590
        """
3591
        if dict_ is None:
1✔
3592
            dict_ = {}
1✔
3593

3594
        self._kwargs += parse_named_injections(dict_)
1✔
3595
        self._kwargs += parse_named_injections(kwargs)
1✔
3596
        self._kwargs_len = len(self._kwargs)
1✔
3597

3598
        return self
1✔
3599

3600
    def set_kwargs(self, dict_=None, **kwargs):
1✔
3601
        """Set keyword argument injections.
3602

3603
        Existing keyword argument injections are dropped.
3604

3605
        :return: Reference ``self``
3606
        """
3607
        if dict_ is None:
1✔
3608
            dict_ = {}
1✔
3609

3610
        self._kwargs = parse_named_injections(dict_)
1✔
3611
        self._kwargs += parse_named_injections(kwargs)
1✔
3612
        self._kwargs_len = len(self._kwargs)
1✔
3613

3614
        return self
1✔
3615

3616
    def clear_kwargs(self):
1✔
3617
        """Drop keyword argument injections.
3618

3619
        :return: Reference ``self``
3620
        """
3621
        self._kwargs = tuple()
1✔
3622
        self._kwargs_len = len(self._kwargs)
1✔
3623
        return self
1✔
3624

3625
    @property
3626
    def related(self):
3627
        """Return related providers generator."""
3628
        yield from filter(is_provider, self.kwargs.values())
1✔
3629
        yield from super().related
1✔
3630

3631
    def _copy_kwargs(self, copied, memo):
1✔
3632
        """Return copy of kwargs."""
3633
        copied_kwargs = {
1✔
3634
            _copy_if_provider(name, memo): _copy_if_provider(value, memo)
1✔
3635
            for name, value in self.kwargs.items()
1✔
3636
        }
3637
        copied.set_kwargs(copied_kwargs)
1✔
3638

3639
    cpdef object _provide(self, tuple args, dict kwargs):
1✔
3640
        """Return result of provided callable call."""
3641
        return __provide_keyword_args(kwargs, self._kwargs, self._kwargs_len, self._async_mode)
1✔
3642

3643

3644

3645
cdef class Resource(Provider):
3646
    """Resource provider provides a component with initialization and shutdown."""
3647

3648
    def __init__(self, provides=None, *args, **kwargs):
3649
        self._provides = None
1✔
3650
        self.set_provides(provides)
1✔
3651

3652
        self._initialized = False
1✔
3653
        self._resource = None
1✔
3654
        self._shutdowner = None
1✔
3655

3656
        self._args = tuple()
1✔
3657
        self._args_len = 0
1✔
3658
        self.set_args(*args)
1✔
3659

3660
        self._kwargs = tuple()
1✔
3661
        self._kwargs_len = 0
1✔
3662
        self.set_kwargs(**kwargs)
1✔
3663

3664
        super().__init__()
1✔
3665

3666
    def __deepcopy__(self, memo):
1✔
3667
        """Create and return full copy of provider."""
3668
        copied = memo.get(id(self))
1✔
3669
        if copied is not None:
1✔
3670
            return copied
×
3671

3672
        if self._initialized:
1✔
3673
            raise Error("Can not copy initialized resource")
1✔
3674

3675
        copied = _memorized_duplicate(self, memo)
1✔
3676
        copied.set_provides(_copy_if_provider(self.provides, memo))
1✔
3677
        copied.set_args(*deepcopy(self.args, memo))
1✔
3678
        copied.set_kwargs(**deepcopy(self.kwargs, memo))
1✔
3679

3680
        self._copy_overridings(copied, memo)
1✔
3681

3682
        return copied
1✔
3683

3684
    def __str__(self):
3685
        """Return string representation of provider.
3686

3687
        :rtype: str
3688
        """
3689
        return represent_provider(provider=self, provides=self.provides)
1✔
3690

3691
    @property
3692
    def provides(self):
3693
        """Return provider provides."""
3694
        return self._provides
1✔
3695

3696
    def set_provides(self, provides):
1✔
3697
        """Set provider provides."""
3698
        provides = _resolve_string_import(provides)
1✔
3699
        self._provides = provides
1✔
3700
        return self
1✔
3701

3702
    @property
3703
    def args(self):
3704
        """Return positional argument injections."""
3705
        cdef int index
3706
        cdef PositionalInjection arg
3707
        cdef list args
3708

3709
        args = list()
1✔
3710
        for index in range(self._args_len):
1✔
3711
            arg = self._args[index]
1✔
3712
            args.append(arg._value)
1✔
3713
        return tuple(args)
1✔
3714

3715
    def add_args(self, *args):
1✔
3716
        """Add positional argument injections.
3717

3718
        :return: Reference ``self``
3719
        """
3720
        self._args += parse_positional_injections(args)
1✔
3721
        self._args_len = len(self._args)
1✔
3722
        return self
1✔
3723

3724
    def set_args(self, *args):
1✔
3725
        """Set positional argument injections.
3726

3727
        Existing positional argument injections are dropped.
3728

3729
        :return: Reference ``self``
3730
        """
3731
        self._args = parse_positional_injections(args)
1✔
3732
        self._args_len = len(self._args)
1✔
3733
        return self
1✔
3734

3735
    def clear_args(self):
1✔
3736
        """Drop positional argument injections.
3737

3738
        :return: Reference ``self``
3739
        """
3740
        self._args = tuple()
1✔
3741
        self._args_len = len(self._args)
1✔
3742
        return self
1✔
3743

3744
    @property
3745
    def kwargs(self):
3746
        """Return keyword argument injections."""
3747
        cdef int index
3748
        cdef NamedInjection kwarg
3749
        cdef dict kwargs
3750

3751
        kwargs = dict()
1✔
3752
        for index in range(self._kwargs_len):
1✔
3753
            kwarg = self._kwargs[index]
1✔
3754
            kwargs[kwarg._name] = kwarg._value
1✔
3755
        return kwargs
1✔
3756

3757
    def add_kwargs(self, **kwargs):
1✔
3758
        """Add keyword argument injections.
3759

3760
        :return: Reference ``self``
3761
        """
3762
        self._kwargs += parse_named_injections(kwargs)
1✔
3763
        self._kwargs_len = len(self._kwargs)
1✔
3764
        return self
1✔
3765

3766
    def set_kwargs(self, **kwargs):
1✔
3767
        """Set keyword argument injections.
3768

3769
        Existing keyword argument injections are dropped.
3770

3771
        :return: Reference ``self``
3772
        """
3773
        self._kwargs = parse_named_injections(kwargs)
1✔
3774
        self._kwargs_len = len(self._kwargs)
1✔
3775
        return self
1✔
3776

3777
    def clear_kwargs(self):
1✔
3778
        """Drop keyword argument injections.
3779

3780
        :return: Reference ``self``
3781
        """
3782
        self._kwargs = tuple()
1✔
3783
        self._kwargs_len = len(self._kwargs)
1✔
3784
        return self
1✔
3785

3786
    @property
3787
    def initialized(self):
3788
        """Check if resource is initialized."""
3789
        return self._initialized
1✔
3790

3791
    def init(self):
1✔
3792
        """Initialize resource."""
3793
        return self.__call__()
1✔
3794

3795
    def shutdown(self):
1✔
3796
        """Shutdown resource."""
3797
        if not self._initialized:
1✔
3798
            if self._async_mode == ASYNC_MODE_ENABLED:
1✔
3799
                result = asyncio.Future()
1✔
3800
                result.set_result(None)
1✔
3801
                return result
1✔
3802
            return
1✔
3803

3804
        if self._shutdowner:
1✔
3805
            try:
1✔
3806
                shutdown = self._shutdowner(self._resource)
1✔
3807
            except StopIteration:
1✔
3808
                pass
3809
            else:
3810
                if inspect.isawaitable(shutdown):
1✔
3811
                    return self._create_shutdown_future(shutdown)
1✔
3812

3813
        self._resource = None
1✔
3814
        self._initialized = False
1✔
3815
        self._shutdowner = None
1✔
3816

3817
        if self._async_mode == ASYNC_MODE_ENABLED:
1✔
3818
            result = asyncio.Future()
1✔
3819
            result.set_result(None)
1✔
3820
            return result
1✔
3821

3822
    @property
3823
    def related(self):
3824
        """Return related providers generator."""
3825
        yield from filter(is_provider, [self.provides])
1✔
3826
        yield from filter(is_provider, self.args)
1✔
3827
        yield from filter(is_provider, self.kwargs.values())
1✔
3828
        yield from super().related
1✔
3829

3830
    cpdef object _provide(self, tuple args, dict kwargs):
1✔
3831
        if self._initialized:
1✔
3832
            return self._resource
1✔
3833

3834
        if self._is_resource_subclass(self._provides):
1✔
3835
            initializer = self._provides()
1✔
3836
            self._resource = __call(
1✔
3837
                initializer.init,
1✔
3838
                args,
3839
                self._args,
1✔
3840
                self._args_len,
3841
                kwargs,
3842
                self._kwargs,
1✔
3843
                self._kwargs_len,
3844
                self._async_mode,
3845
            )
3846
            self._shutdowner = initializer.shutdown
1✔
3847
        elif self._is_async_resource_subclass(self._provides):
1✔
3848
            initializer = self._provides()
1✔
3849
            async_init = __call(
1✔
3850
                initializer.init,
1✔
3851
                args,
3852
                self._args,
1✔
3853
                self._args_len,
3854
                kwargs,
3855
                self._kwargs,
1✔
3856
                self._kwargs_len,
3857
                self._async_mode,
3858
            )
3859
            self._initialized = True
1✔
3860
            return self._create_init_future(async_init, initializer.shutdown)
1✔
3861
        elif inspect.isgeneratorfunction(self._provides):
1✔
3862
            initializer = __call(
1✔
3863
                self._provides,
1✔
3864
                args,
3865
                self._args,
1✔
3866
                self._args_len,
3867
                kwargs,
3868
                self._kwargs,
1✔
3869
                self._kwargs_len,
3870
                self._async_mode,
3871
            )
3872
            self._resource = next(initializer)
1✔
3873
            self._shutdowner = initializer.send
1✔
3874
        elif iscoroutinefunction(self._provides):
1✔
3875
            initializer = __call(
1✔
3876
                self._provides,
1✔
3877
                args,
3878
                self._args,
1✔
3879
                self._args_len,
3880
                kwargs,
3881
                self._kwargs,
1✔
3882
                self._kwargs_len,
3883
                self._async_mode,
3884
            )
3885
            self._initialized = True
1✔
3886
            return self._create_init_future(initializer)
1✔
3887
        elif isasyncgenfunction(self._provides):
1✔
3888
            initializer = __call(
1✔
3889
                self._provides,
1✔
3890
                args,
3891
                self._args,
1✔
3892
                self._args_len,
3893
                kwargs,
3894
                self._kwargs,
1✔
3895
                self._kwargs_len,
3896
                self._async_mode,
3897
            )
3898
            self._initialized = True
1✔
3899
            return self._create_async_gen_init_future(initializer)
1✔
3900
        elif callable(self._provides):
1✔
3901
            self._resource = __call(
1✔
3902
                self._provides,
1✔
3903
                args,
3904
                self._args,
1✔
3905
                self._args_len,
3906
                kwargs,
3907
                self._kwargs,
1✔
3908
                self._kwargs_len,
3909
                self._async_mode,
3910
            )
3911
        else:
3912
            raise Error("Unknown type of resource initializer")
1✔
3913

3914
        self._initialized = True
1✔
3915
        return self._resource
1✔
3916

3917
    def _create_init_future(self, future, shutdowner=None):
1✔
3918
        callback = self._async_init_callback
1✔
3919
        if shutdowner:
1✔
3920
            callback = functools.partial(callback, shutdowner=shutdowner)
1✔
3921

3922
        future = asyncio.ensure_future(future)
1✔
3923
        future.add_done_callback(callback)
1✔
3924
        self._resource = future
1✔
3925

3926
        return future
1✔
3927

3928
    def _create_async_gen_init_future(self, initializer):
1✔
3929
        if inspect.isasyncgen(initializer):
1✔
3930
            return self._create_init_future(initializer.__anext__(), initializer.asend)
1✔
3931

3932
        future = asyncio.Future()
1✔
3933

3934
        create_initializer = asyncio.ensure_future(initializer)
1✔
3935
        create_initializer.add_done_callback(functools.partial(self._async_create_gen_callback, future))
1✔
3936
        self._resource = future
1✔
3937

3938
        return future
1✔
3939

3940
    def _async_init_callback(self, initializer, shutdowner=None):
1✔
3941
        try:
1✔
3942
            resource = initializer.result()
1✔
3943
        except Exception:
1✔
3944
            self._initialized = False
1✔
3945
        else:
3946
            self._resource = resource
1✔
3947
            self._shutdowner = shutdowner
1✔
3948

3949
    def _async_create_gen_callback(self, future, initializer_future):
1✔
3950
        initializer = initializer_future.result()
1✔
3951
        init_future = self._create_init_future(initializer.__anext__(), initializer.asend)
1✔
3952
        init_future.add_done_callback(functools.partial(self._async_trigger_result, future))
1✔
3953

3954
    def _async_trigger_result(self, future, future_result):
1✔
3955
        future.set_result(future_result.result())
1✔
3956

3957
    def _create_shutdown_future(self, shutdown_future):
1✔
3958
        future = asyncio.Future()
1✔
3959
        shutdown_future = asyncio.ensure_future(shutdown_future)
1✔
3960
        shutdown_future.add_done_callback(functools.partial(self._async_shutdown_callback, future))
1✔
3961
        return future
1✔
3962

3963
    def _async_shutdown_callback(self, future_result, shutdowner):
1✔
3964
        try:
1✔
3965
            shutdowner.result()
1✔
3966
        except StopAsyncIteration:
1✔
3967
            pass
3968

3969
        self._resource = None
1✔
3970
        self._initialized = False
1✔
3971
        self._shutdowner = None
1✔
3972

3973
        future_result.set_result(None)
1✔
3974

3975
    @staticmethod
1✔
3976
    def _is_resource_subclass(instance):
3977
        if  sys.version_info < (3, 5):
1✔
3978
            return False
×
3979
        if not isinstance(instance, CLASS_TYPES):
1✔
3980
            return
1✔
3981
        from . import resources
1✔
3982
        return issubclass(instance, resources.Resource)
1✔
3983

3984
    @staticmethod
1✔
3985
    def _is_async_resource_subclass(instance):
3986
        if  sys.version_info < (3, 5):
1✔
3987
            return False
×
3988
        if not isinstance(instance, CLASS_TYPES):
1✔
3989
            return
1✔
3990
        from . import resources
1✔
3991
        return issubclass(instance, resources.AsyncResource)
1✔
3992

3993

3994
cdef class Container(Provider):
3995
    """Container provider provides an instance of declarative container.
3996

3997
    .. warning::
3998
        Provider is experimental. Its interface may change.
3999
    """
4000

4001
    def __init__(self, container_cls=None, container=None, **overriding_providers):
4002
        """Initialize provider."""
4003
        self._container_cls = container_cls
1✔
4004
        self._overriding_providers = overriding_providers
1✔
4005

4006
        if container is None and container_cls:
1✔
4007
            container = container_cls()
1✔
4008
            container.assign_parent(self)
1✔
4009
        self._container = container
1✔
4010

4011
        if self._container and self._overriding_providers:
1✔
4012
            self.apply_overridings()
1✔
4013

4014
        self._parent = None
1✔
4015

4016
        super(Container, self).__init__()
1✔
4017

4018
    def __deepcopy__(self, memo):
1✔
4019
        """Create and return full copy of provider."""
4020
        cdef Container copied
4021

4022
        copied = memo.get(id(self))
1✔
4023
        if copied is not None:
1✔
4024
            return copied
×
4025

4026
        copied = <Container> _memorized_duplicate(self, memo)
1✔
4027
        copied._container_cls = self._container_cls
1✔
4028
        copied._container = deepcopy(self._container, memo)
1✔
4029
        copied._overriding_providers = deepcopy(self._overriding_providers, memo)
1✔
4030
        self._copy_parent(copied, memo)
1✔
4031
        self._copy_overridings(copied, memo)
1✔
4032
        return copied
1✔
4033

4034
    def __getattr__(self, name):
4035
        """Return dependency provider."""
4036
        if name.startswith("__") and name.endswith("__"):
1✔
4037
            raise AttributeError(
1✔
4038
                "'{cls}' object has no attribute "
4039
                "'{attribute_name}'".format(cls=self.__class__.__name__, attribute_name=name))
1✔
4040
        return getattr(self._container, name)
1✔
4041

4042
    @property
4043
    def providers(self):
4044
        return self._container.providers
1✔
4045

4046
    @property
4047
    def container(self):
4048
        return self._container
1✔
4049

4050
    def override(self, provider):
1✔
4051
        """Override provider with another provider."""
4052
        if not hasattr(provider, "providers"):
1✔
4053
            raise Error("Container provider {0} can be overridden only by providers container".format(self))
1✔
4054

4055
        self._container.override_providers(**provider.providers)
1✔
4056
        return super().override(provider)
1✔
4057

4058
    def reset_last_overriding(self):
1✔
4059
        """Reset last overriding provider.
4060

4061
        :raise: :py:exc:`dependency_injector.errors.Error` if provider is not
4062
                overridden.
4063

4064
        :rtype: None
4065
        """
4066
        super().reset_last_overriding()
1✔
4067
        for provider in self._container.providers.values():
1✔
4068
            if not provider.overridden:
1✔
4069
                continue
1✔
4070
            provider.reset_last_overriding()
1✔
4071

4072
    def reset_override(self):
1✔
4073
        """Reset all overriding providers.
4074

4075
        :rtype: None
4076
        """
4077
        super().reset_override()
1✔
4078
        for provider in self._container.providers.values():
1✔
4079
            if not provider.overridden:
1✔
4080
                continue
1✔
4081
            provider.reset_override()
1✔
4082

4083
    def apply_overridings(self):
1✔
4084
        """Apply container overriding.
4085

4086
        This method should not be called directly. It is called on
4087
        declarative container initialization."""
4088
        self._container.override_providers(**self._overriding_providers)
1✔
4089

4090
    @property
4091
    def related(self):
4092
        """Return related providers generator."""
4093
        yield from self.providers.values()
1✔
4094
        yield from super().related
1✔
4095

4096
    def resolve_provider_name(self, provider):
1✔
4097
        """Try to resolve provider name."""
4098
        for provider_name, container_provider in self.providers.items():
1✔
4099
            if container_provider is provider:
1✔
4100
                return provider_name
1✔
4101
        else:
4102
            raise Error(f"Can not resolve name for provider \"{provider}\"")
1✔
4103

4104
    @property
4105
    def parent(self):
4106
        """Return parent."""
4107
        return self._parent
1✔
4108

4109
    @property
4110
    def parent_name(self):
4111
        """Return parent name."""
4112
        if not self._parent:
1✔
4113
            return None
1✔
4114

4115
        name = ""
1✔
4116
        if self._parent.parent_name:
1✔
4117
            name += f"{self._parent.parent_name}."
1✔
4118
        name += f"{self._parent.resolve_provider_name(self)}"
1✔
4119

4120
        return name
1✔
4121

4122
    def assign_parent(self, parent):
1✔
4123
        """Assign parent."""
4124
        self._parent = parent
1✔
4125

4126
    def _copy_parent(self, copied, memo):
1✔
4127
        _copy_parent(self, copied, memo)
1✔
4128

4129
    cpdef object _provide(self, tuple args, dict kwargs):
1✔
4130
        """Return single instance."""
4131
        return self._container
1✔
4132

4133

4134
cdef class Selector(Provider):
4135
    """Selector provider selects provider based on the configuration value or other callable.
4136

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

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

4143
    The providers are provided as keyword arguments. Argument name is used as a key for
4144
    selecting the provider.
4145

4146
    .. code-block:: python
4147

4148
        config = Configuration()
4149

4150
        selector = Selector(
4151
            config.one_or_another,
4152
            one=providers.Factory(SomeClass),
4153
            another=providers.Factory(SomeOtherClass),
4154
        )
4155

4156
        config.override({"one_or_another": "one"})
4157
        instance_1 = selector()
4158
        assert isinstance(instance_1, SomeClass)
4159

4160
        config.override({"one_or_another": "another"})
4161
        instance_2 = selector()
4162
        assert isinstance(instance_2, SomeOtherClass)
4163
    """
4164

4165
    def __init__(self, selector=None, **providers):
4166
        """Initialize provider."""
4167
        self._selector = None
1✔
4168
        self.set_selector(selector)
1✔
4169

4170
        self._providers = {}
1✔
4171
        self.set_providers(**providers)
1✔
4172

4173
        super(Selector, self).__init__()
1✔
4174

4175
    def __deepcopy__(self, memo):
1✔
4176
        """Create and return full copy of provider."""
4177
        copied = memo.get(id(self))
1✔
4178
        if copied is not None:
1✔
4179
            return copied
×
4180

4181
        copied = _memorized_duplicate(self, memo)
1✔
4182
        copied.set_selector(deepcopy(self._selector, memo))
1✔
4183
        copied.set_providers(**deepcopy(self._providers, memo))
1✔
4184

4185
        self._copy_overridings(copied, memo)
1✔
4186

4187
        return copied
1✔
4188

4189
    def __getattr__(self, name):
4190
        """Return provider."""
4191
        if name.startswith("__") and name.endswith("__"):
1✔
4192
            raise AttributeError(
1✔
4193
                "'{cls}' object has no attribute "
4194
                "'{attribute_name}'".format(cls=self.__class__.__name__, attribute_name=name))
1✔
4195
        if name not in self._providers:
1✔
4196
            raise AttributeError("Selector has no \"{0}\" provider".format(name))
1✔
4197

4198
        return self._providers[name]
1✔
4199

4200
    def __str__(self):
4201
        """Return string representation of provider.
4202

4203
        :rtype: str
4204
        """
4205

4206
        return "<{provider}({selector}, {providers}) at {address}>".format(
1✔
4207
            provider=".".join(( self.__class__.__module__, self.__class__.__name__)),
1✔
4208
            selector=self._selector,
1✔
4209
            providers=", ".join((
1✔
4210
                "{0}={1}".format(name, provider)
1✔
4211
                for name, provider in self._providers.items()
1✔
4212
            )),
4213
            address=hex(id(self)),
1✔
4214
        )
4215

4216
    @property
4217
    def selector(self):
4218
        """Return selector."""
4219
        return self._selector
1✔
4220

4221
    def set_selector(self, selector):
1✔
4222
        """Set selector."""
4223
        self._selector = selector
1✔
4224
        return self
1✔
4225

4226
    @property
4227
    def providers(self):
4228
        """Return providers."""
4229
        return dict(self._providers)
1✔
4230

4231
    def set_providers(self, **providers: Provider):
1✔
4232
        """Set providers."""
4233
        self._providers = providers
1✔
4234
        return self
1✔
4235

4236
    @property
4237
    def related(self):
4238
        """Return related providers generator."""
4239
        yield from filter(is_provider, [self._selector])
1✔
4240
        yield from self.providers.values()
1✔
4241
        yield from super().related
1✔
4242

4243
    cpdef object _provide(self, tuple args, dict kwargs):
1✔
4244
        """Return single instance."""
4245
        selector_value = self._selector()
1✔
4246

4247
        if selector_value is None:
1✔
4248
            raise Error("Selector value is undefined")
1✔
4249

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

4253
        return self._providers[selector_value](*args, **kwargs)
1✔
4254

4255

4256
cdef class ProvidedInstance(Provider):
4257
    """Provider that helps to inject attributes and items of the injected instance.
4258

4259
    You can use it like that:
4260

4261
    .. code-block:: python
4262

4263
       service = providers.Singleton(Service)
4264

4265
       client_factory = providers.Factory(
4266
           Client,
4267
           value1=service.provided[0],
4268
           value2=service.provided.value,
4269
           value3=service.provided.values[0],
4270
           value4=service.provided.get_value.call(),
4271
       )
4272

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

4276
    Providers that have ``.provided`` attribute:
4277

4278
    - :py:class:`Callable` and its subclasses
4279
    - :py:class:`Factory` and its subclasses
4280
    - :py:class:`Singleton` and its subclasses
4281
    - :py:class:`Object`
4282
    - :py:class:`List`
4283
    - :py:class:`Selector`
4284
    - :py:class:`Dependency`
4285
    """
4286

4287
    def __init__(self, provides=None):
4288
        self._provides = None
1✔
4289
        self.set_provides(provides)
1✔
4290
        super().__init__()
1✔
4291

4292
    def __repr__(self):
4293
        return f"{self.__class__.__name__}(\"{self._provides}\")"
1✔
4294

4295
    def __deepcopy__(self, memo):
1✔
4296
        copied = memo.get(id(self))
1✔
4297
        if copied is not None:
1✔
4298
            return copied
×
4299

4300
        copied = _memorized_duplicate(self, memo)
1✔
4301
        copied.set_provides(_copy_if_provider(self.provides, memo))
1✔
4302
        return copied
1✔
4303

4304
    def __getattr__(self, item):
4305
        return AttributeGetter(self, item)
1✔
4306

4307
    def __getitem__(self, item):
4308
        return ItemGetter(self, item)
1✔
4309

4310
    @property
4311
    def provides(self):
4312
        """Return provider provides."""
4313
        return self._provides
1✔
4314

4315
    def set_provides(self, provides):
1✔
4316
        """Set provider provides."""
4317
        self._provides = provides
1✔
4318
        return self
1✔
4319

4320
    def call(self, *args, **kwargs):
1✔
4321
        return MethodCaller(self, *args, **kwargs)
1✔
4322

4323
    @property
4324
    def related(self):
4325
        """Return related providers generator."""
4326
        if is_provider(self.provides):
1✔
4327
            yield self.provides
1✔
4328
        yield from super().related
1✔
4329

4330
    cpdef object _provide(self, tuple args, dict kwargs):
1✔
4331
        return self._provides(*args, **kwargs)
1✔
4332

4333

4334
cdef class AttributeGetter(Provider):
4335
    """Provider that returns the attribute of the injected instance.
4336

4337
    You should not create this provider directly. See :py:class:`ProvidedInstance` instead.
4338
    """
4339

4340
    def __init__(self, provides=None, name=None):
4341
        self._provides = None
1✔
4342
        self.set_provides(provides)
1✔
4343

4344
        self._name = None
1✔
4345
        self.set_name(name)
1✔
4346
        super().__init__()
1✔
4347

4348
    def __repr__(self):
4349
        return f"{self.__class__.__name__}(\"{self.name}\")"
1✔
4350

4351
    def __deepcopy__(self, memo):
1✔
4352
        copied = memo.get(id(self))
1✔
4353
        if copied is not None:
1✔
4354
            return copied
×
4355

4356
        copied = _memorized_duplicate(self, memo)
1✔
4357
        copied.set_provides(_copy_if_provider(self.provides, memo))
1✔
4358
        copied.set_name(self.name)
1✔
4359
        return copied
1✔
4360

4361
    def __getattr__(self, item):
4362
        return AttributeGetter(self, item)
1✔
4363

4364
    def __getitem__(self, item):
4365
        return ItemGetter(self, item)
1✔
4366

4367
    @property
4368
    def provides(self):
4369
        """Return provider provides."""
4370
        return self._provides
1✔
4371

4372
    def set_provides(self, provides):
1✔
4373
        """Set provider provides."""
4374
        self._provides = provides
1✔
4375
        return self
1✔
4376

4377
    @property
4378
    def name(self):
4379
        """Return name of the attribute."""
4380
        return self._name
1✔
4381

4382
    def set_name(self, name):
1✔
4383
        """Set name of the attribute."""
4384
        self._name = name
1✔
4385
        return self
1✔
4386

4387
    def call(self, *args, **kwargs):
1✔
4388
        return MethodCaller(self, *args, **kwargs)
1✔
4389

4390
    @property
4391
    def related(self):
4392
        """Return related providers generator."""
4393
        if is_provider(self.provides):
1✔
4394
            yield self.provides
1✔
4395
        yield from super().related
1✔
4396

4397
    cpdef object _provide(self, tuple args, dict kwargs):
1✔
4398
        provided = self.provides(*args, **kwargs)
1✔
4399
        if __is_future_or_coroutine(provided):
1✔
4400
            future_result = asyncio.Future()
1✔
4401
            provided = asyncio.ensure_future(provided)
1✔
4402
            provided.add_done_callback(functools.partial(self._async_provide, future_result))
1✔
4403
            return future_result
1✔
4404
        return getattr(provided, self.name)
1✔
4405

4406
    def _async_provide(self, future_result, future):
1✔
4407
        try:
1✔
4408
            provided = future.result()
1✔
4409
            result = getattr(provided, self.name)
1✔
4410
        except Exception as exception:
1✔
4411
            future_result.set_exception(exception)
1✔
4412
        else:
4413
            future_result.set_result(result)
1✔
4414

4415

4416
cdef class ItemGetter(Provider):
4417
    """Provider that returns the item of the injected instance.
4418

4419
    You should not create this provider directly. See :py:class:`ProvidedInstance` instead.
4420
    """
4421

4422
    def __init__(self, provides=None, name=None):
4423
        self._provides = None
1✔
4424
        self.set_provides(provides)
1✔
4425

4426
        self._name = None
1✔
4427
        self.set_name(name)
1✔
4428
        super().__init__()
1✔
4429

4430
    def __repr__(self):
4431
        return f"{self.__class__.__name__}(\"{self.name}\")"
1✔
4432

4433
    def __deepcopy__(self, memo):
1✔
4434
        copied = memo.get(id(self))
1✔
4435
        if copied is not None:
1✔
4436
            return copied
×
4437

4438
        copied = _memorized_duplicate(self, memo)
1✔
4439
        copied.set_provides(_copy_if_provider(self.provides, memo))
1✔
4440
        copied.set_name(self.name)
1✔
4441
        return copied
1✔
4442

4443
    def __getattr__(self, item):
4444
        return AttributeGetter(self, item)
1✔
4445

4446
    def __getitem__(self, item):
4447
        return ItemGetter(self, item)
1✔
4448

4449
    @property
4450
    def provides(self):
4451
        """Return provider"s provides."""
4452
        return self._provides
1✔
4453

4454
    def set_provides(self, provides):
1✔
4455
        """Set provider"s provides."""
4456
        self._provides = provides
1✔
4457
        return self
1✔
4458

4459
    @property
4460
    def name(self):
4461
        """Return name of the item."""
4462
        return self._name
1✔
4463

4464
    def set_name(self, name):
1✔
4465
        """Set name of the item."""
4466
        self._name = name
1✔
4467
        return self
1✔
4468

4469
    def call(self, *args, **kwargs):
1✔
4470
        return MethodCaller(self, *args, **kwargs)
1✔
4471

4472
    @property
4473
    def related(self):
4474
        """Return related providers generator."""
4475
        if is_provider(self.provides):
1✔
4476
            yield self.provides
1✔
4477
        yield from super().related
1✔
4478

4479
    cpdef object _provide(self, tuple args, dict kwargs):
1✔
4480
        provided = self.provides(*args, **kwargs)
1✔
4481
        if __is_future_or_coroutine(provided):
1✔
4482
            future_result = asyncio.Future()
1✔
4483
            provided = asyncio.ensure_future(provided)
1✔
4484
            provided.add_done_callback(functools.partial(self._async_provide, future_result))
1✔
4485
            return future_result
1✔
4486
        return provided[self.name]
1✔
4487

4488
    def _async_provide(self, future_result, future):
1✔
4489
        try:
1✔
4490
            provided = future.result()
1✔
4491
            result = provided[self.name]
1✔
4492
        except Exception as exception:
1✔
4493
            future_result.set_exception(exception)
1✔
4494
        else:
4495
            future_result.set_result(result)
1✔
4496

4497

4498
cdef class MethodCaller(Provider):
4499
    """Provider that calls the method of the injected instance.
4500

4501
    You should not create this provider directly. See :py:class:`ProvidedInstance` instead.
4502
    """
4503

4504
    def __init__(self, provides=None, *args, **kwargs):
4505
        self._provides = None
1✔
4506
        self.set_provides(provides)
1✔
4507

4508
        self._args = tuple()
1✔
4509
        self._args_len = 0
1✔
4510
        self.set_args(*args)
1✔
4511

4512
        self._kwargs = tuple()
1✔
4513
        self._kwargs_len = 0
1✔
4514
        self.set_kwargs(**kwargs)
1✔
4515

4516
        super().__init__()
1✔
4517

4518
    def __repr__(self):
4519
        return f"{self.__class__.__name__}({self.provides})"
×
4520

4521
    def __deepcopy__(self, memo):
1✔
4522
        copied = memo.get(id(self))
1✔
4523
        if copied is not None:
1✔
4524
            return copied
×
4525

4526
        copied = _memorized_duplicate(self, memo)
1✔
4527
        copied.set_provides(_copy_if_provider(self.provides, memo))
1✔
4528
        copied.set_args(*deepcopy(self.args, memo))
1✔
4529
        copied.set_kwargs(**deepcopy(self.kwargs, memo))
1✔
4530
        self._copy_overridings(copied, memo)
1✔
4531
        return copied
1✔
4532

4533
    def __getattr__(self, item):
4534
        return AttributeGetter(self, item)
1✔
4535

4536
    def __getitem__(self, item):
4537
        return ItemGetter(self, item)
1✔
4538

4539
    def call(self, *args, **kwargs):
1✔
4540
        return MethodCaller(self, *args, **kwargs)
1✔
4541

4542
    @property
4543
    def provides(self):
4544
        """Return provider provides."""
4545
        return self._provides
1✔
4546

4547
    def set_provides(self, provides):
1✔
4548
        """Set provider provides."""
4549
        self._provides = provides
1✔
4550
        return self
1✔
4551

4552
    @property
4553
    def args(self):
4554
        """Return positional argument injections."""
4555
        cdef int index
4556
        cdef PositionalInjection arg
4557
        cdef list args
4558

4559
        args = list()
1✔
4560
        for index in range(self._args_len):
1✔
4561
            arg = self._args[index]
1✔
4562
            args.append(arg._value)
1✔
4563
        return tuple(args)
1✔
4564

4565
    def set_args(self, *args):
1✔
4566
        """Set positional argument injections.
4567

4568
        Existing positional argument injections are dropped.
4569

4570
        :return: Reference ``self``
4571
        """
4572
        self._args = parse_positional_injections(args)
1✔
4573
        self._args_len = len(self._args)
1✔
4574
        return self
1✔
4575

4576
    @property
4577
    def kwargs(self):
4578
        """Return keyword argument injections."""
4579
        cdef int index
4580
        cdef NamedInjection kwarg
4581
        cdef dict kwargs
4582

4583
        kwargs = dict()
1✔
4584
        for index in range(self._kwargs_len):
1✔
4585
            kwarg = self._kwargs[index]
1✔
4586
            kwargs[kwarg._name] = kwarg._value
1✔
4587
        return kwargs
1✔
4588

4589
    def set_kwargs(self, **kwargs):
1✔
4590
        """Set keyword argument injections.
4591

4592
        Existing keyword argument injections are dropped.
4593

4594
        :return: Reference ``self``
4595
        """
4596
        self._kwargs = parse_named_injections(kwargs)
1✔
4597
        self._kwargs_len = len(self._kwargs)
1✔
4598
        return self
1✔
4599

4600
    @property
4601
    def related(self):
4602
        """Return related providers generator."""
4603
        if is_provider(self.provides):
1✔
4604
            yield self.provides
1✔
4605
        yield from filter(is_provider, self.args)
1✔
4606
        yield from filter(is_provider, self.kwargs.values())
1✔
4607
        yield from super().related
1✔
4608

4609
    cpdef object _provide(self, tuple args, dict kwargs):
1✔
4610
        call = self.provides()
1✔
4611
        if __is_future_or_coroutine(call):
1✔
4612
            future_result = asyncio.Future()
1✔
4613
            call = asyncio.ensure_future(call)
1✔
4614
            call.add_done_callback(functools.partial(self._async_provide, future_result, args, kwargs))
1✔
4615
            return future_result
1✔
4616
        return __call(
1✔
4617
            call,
4618
            args,
4619
            self._args,
1✔
4620
            self._args_len,
4621
            kwargs,
4622
            self._kwargs,
1✔
4623
            self._kwargs_len,
4624
            self._async_mode,
4625
        )
4626

4627
    def _async_provide(self, future_result, args, kwargs, future):
1✔
4628
        try:
1✔
4629
            call = future.result()
1✔
4630
            result = __call(
1✔
4631
                call,
4632
                args,
1✔
4633
                self._args,
1✔
4634
                self._args_len,
4635
                kwargs,
1✔
4636
                self._kwargs,
1✔
4637
                self._kwargs_len,
4638
                self._async_mode,
4639
            )
4640
        except Exception as exception:
1✔
4641
            future_result.set_exception(exception)
1✔
4642
        else:
4643
            future_result.set_result(result)
1✔
4644

4645

4646
cdef class Injection(object):
4647
    """Abstract injection class."""
4648

4649

4650
cdef class PositionalInjection(Injection):
4651
    """Positional injection class."""
4652

4653
    def __init__(self, value=None):
4654
        """Initializer."""
4655
        self._value = None
1✔
4656
        self._is_provider = 0
1✔
4657
        self._is_delegated = 0
1✔
4658
        self._call = 0
1✔
4659
        self.set(value)
1✔
4660
        super(PositionalInjection, 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(_copy_if_provider(self._value, memo))
1✔
4669
        return copied
1✔
4670

4671
    def get_value(self):
1✔
4672
        """Return injection value."""
4673
        return __get_value(self)
1✔
4674

4675
    def get_original_value(self):
1✔
4676
        """Return original value."""
4677
        return self._value
1✔
4678

4679
    def set(self, value):
1✔
4680
        """Set injection."""
4681
        self._value = value
1✔
4682
        self._is_provider = <int>is_provider(value)
1✔
4683
        self._is_delegated = <int>is_delegated(value)
1✔
4684
        self._call = <int>(self._is_provider == 1 and self._is_delegated == 0)
1✔
4685

4686

4687
cdef class NamedInjection(Injection):
4688
    """Keyword injection class."""
4689

4690
    def __init__(self, name=None, value=None):
4691
        """Initializer."""
4692
        self._name = name
1✔
4693
        self.set_name(name)
1✔
4694

4695
        self._value = None
1✔
4696
        self._is_provider = 0
1✔
4697
        self._is_delegated = 0
1✔
4698
        self._call = 0
1✔
4699
        self.set(value)
1✔
4700

4701
        super(NamedInjection, self).__init__()
1✔
4702

4703
    def __deepcopy__(self, memo):
1✔
4704
        """Create and return full copy of provider."""
4705
        copied = memo.get(id(self))
1✔
4706
        if copied is not None:
1✔
4707
            return copied
×
4708
        copied = _memorized_duplicate(self, memo)
1✔
4709
        copied.set_name(self.get_name())
1✔
4710
        copied.set(_copy_if_provider(self._value, memo))
1✔
4711
        return copied
1✔
4712

4713
    def get_name(self):
1✔
4714
        """Return injection name."""
4715
        return __get_name(self)
1✔
4716

4717
    def set_name(self, name):
1✔
4718
        """Set injection name."""
4719
        self._name = name
1✔
4720

4721
    def get_value(self):
1✔
4722
        """Return injection value."""
4723
        return __get_value(self)
1✔
4724

4725
    def get_original_value(self):
1✔
4726
        """Return original value."""
4727
        return self._value
1✔
4728

4729
    def set(self, value):
1✔
4730
        """Set injection."""
4731
        self._value = value
1✔
4732
        self._is_provider = <int>is_provider(value)
1✔
4733
        self._is_delegated = <int>is_delegated(value)
1✔
4734
        self._call = <int>(self._is_provider == 1 and self._is_delegated == 0)
1✔
4735

4736

4737
@cython.boundscheck(False)
4738
@cython.wraparound(False)
4739
cpdef tuple parse_positional_injections(tuple args):
1✔
4740
    """Parse positional injections."""
4741
    cdef list injections = list()
1✔
4742
    cdef int args_len = len(args)
1✔
4743

4744
    cdef int index
4745
    cdef object arg
4746
    cdef PositionalInjection injection
4747

4748
    for index in range(args_len):
1✔
4749
        arg = args[index]
1✔
4750
        injection = PositionalInjection(arg)
1✔
4751
        injections.append(injection)
1✔
4752

4753
    return tuple(injections)
1✔
4754

4755

4756
@cython.boundscheck(False)
4757
@cython.wraparound(False)
4758
cpdef tuple parse_named_injections(dict kwargs):
1✔
4759
    """Parse named injections."""
4760
    cdef list injections = list()
1✔
4761

4762
    cdef object name
4763
    cdef object arg
4764
    cdef NamedInjection injection
4765

4766
    for name, arg in kwargs.items():
1✔
4767
        injection = NamedInjection(name, arg)
1✔
4768
        injections.append(injection)
1✔
4769

4770
    return tuple(injections)
1✔
4771

4772

4773
cdef class OverridingContext(object):
4774
    """Provider overriding context.
4775

4776
    :py:class:`OverridingContext` is used by :py:meth:`Provider.override` for
4777
    implementing ``with`` contexts. When :py:class:`OverridingContext` is
4778
    closed, overriding that was created in this context is dropped also.
4779

4780
    .. code-block:: python
4781

4782
        with provider.override(another_provider):
4783
            assert provider.overridden
4784
        assert not provider.overridden
4785
    """
4786

4787
    def __init__(self, Provider overridden, Provider overriding):
4788
        """Initializer.
4789

4790
        :param overridden: Overridden provider.
4791
        :type overridden: :py:class:`Provider`
4792

4793
        :param overriding: Overriding provider.
4794
        :type overriding: :py:class:`Provider`
4795
        """
4796
        self._overridden = overridden
1✔
4797
        self._overriding = overriding
1✔
4798
        super(OverridingContext, self).__init__()
1✔
4799

4800
    def __enter__(self):
1✔
4801
        """Do nothing."""
4802
        return self._overriding
1✔
4803

4804
    def __exit__(self, *_):
1✔
4805
        """Exit overriding context."""
4806
        self._overridden.reset_last_overriding()
1✔
4807

4808

4809
cdef class BaseSingletonResetContext(object):
4810

4811
    def __init__(self, Provider provider):
4812
        self._singleton = provider
1✔
4813
        super().__init__()
1✔
4814

4815
    def __enter__(self):
1✔
4816
        return self._singleton
1✔
4817

4818
    def __exit__(self, *_):
1✔
4819
        raise NotImplementedError()
×
4820

4821

4822
cdef class SingletonResetContext(BaseSingletonResetContext):
4823

4824
    def __exit__(self, *_):
1✔
4825
        return self._singleton.reset()
1✔
4826

4827

4828
cdef class SingletonFullResetContext(BaseSingletonResetContext):
4829

4830
    def __exit__(self, *_):
1✔
4831
        return self._singleton.full_reset()
1✔
4832

4833

4834
CHILD_PROVIDERS = (Dependency, DependenciesContainer, Container)
1✔
4835

4836

4837
cpdef bint is_provider(object instance):
1✔
4838
    """Check if instance is provider instance.
4839

4840
    :param instance: Instance to be checked.
4841
    :type instance: object
4842

4843
    :rtype: bool
4844
    """
4845
    return (not isinstance(instance, CLASS_TYPES) and
1✔
4846
            getattr(instance, "__IS_PROVIDER__", False) is True)
1✔
4847

4848

4849
cpdef object ensure_is_provider(object instance):
1✔
4850
    """Check if instance is provider instance and return it.
4851

4852
    :param instance: Instance to be checked.
4853
    :type instance: object
4854

4855
    :raise: :py:exc:`dependency_injector.errors.Error` if provided instance is
4856
            not provider.
4857

4858
    :rtype: :py:class:`dependency_injector.providers.Provider`
4859
    """
4860
    if not is_provider(instance):
1✔
4861
        raise Error("Expected provider instance, got {0}".format(str(instance)))
1✔
4862
    return instance
1✔
4863

4864

4865
cpdef bint is_delegated(object instance):
1✔
4866
    """Check if instance is delegated provider.
4867

4868
    :param instance: Instance to be checked.
4869
    :type instance: object
4870

4871
    :rtype: bool
4872
    """
4873
    return (not isinstance(instance, CLASS_TYPES) and
1✔
4874
            getattr(instance, "__IS_DELEGATED__", False) is True)
1✔
4875

4876

4877
cpdef str represent_provider(object provider, object provides):
1✔
4878
    """Return string representation of provider.
4879

4880
    :param provider: Provider object
4881
    :type provider: :py:class:`dependency_injector.providers.Provider`
4882

4883
    :param provides: Object that provider provides
4884
    :type provider: object
4885

4886
    :return: String representation of provider
4887
    :rtype: str
4888
    """
4889
    return "<{provider}({provides}) at {address}>".format(
1✔
4890
        provider=".".join((provider.__class__.__module__,
1✔
4891
                           provider.__class__.__name__)),
1✔
4892
        provides=repr(provides) if provides is not None else "",
1✔
4893
        address=hex(id(provider)))
1✔
4894

4895

4896
cpdef bint is_container_instance(object instance):
1✔
4897
    """Check if instance is container instance.
4898

4899
    :param instance: Instance to be checked.
4900
    :type instance: object
4901

4902
    :rtype: bool
4903
    """
4904
    return (not isinstance(instance, CLASS_TYPES) and
1✔
4905
            getattr(instance, "__IS_CONTAINER__", False) is True)
1✔
4906

4907

4908
cpdef bint is_container_class(object instance):
1✔
4909
    """Check if instance is container class.
4910

4911
    :param instance: Instance to be checked.
4912
    :type instance: object
4913

4914
    :rtype: bool
4915
    """
4916
    return (isinstance(instance, CLASS_TYPES) and
×
4917
            getattr(instance, "__IS_CONTAINER__", False) is True)
×
4918

4919

4920
cpdef object deepcopy(object instance, dict memo=None):
1✔
4921
    """Return full copy of provider or container with providers."""
4922
    if memo is None:
1✔
4923
        memo = dict()
1✔
4924

4925
    __add_sys_streams(memo)
1✔
4926

4927
    return copy.deepcopy(instance, memo)
1✔
4928

4929

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

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

4940

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

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

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

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

4961

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

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

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

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

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

4982
        yield visiting
1✔
4983

4984

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

4992

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

5000

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

5008

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

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

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

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

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

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

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

5035

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

5041

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

5046

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

5056

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

5062

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

5068

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