# Migrating from 2.\* to 3.\*

## How to Read This Guide

This guide is intended to help you migrate existing functionality from `that-depends` version `2.*` to `3.*`.  
The goal is to enable you to migrate as quickly as possible while making only the minimal necessary changes to your codebase.

If you want to learn more about the new features introduced in `3.*`, please refer to the [documentation](https://that-depends.readthedocs.io/) and the [release notes](https://github.com/modern-python/that-depends/releases).

---

## Deprecated or Removed Features

### **`container_context()`** can no longer be initialized without arguments.

Previously the following code would reset the context for all all providers in all containers:
```python
async with container_context():
    # all ContextResources have been reset
    ...
```
This was done to enable easy migration and compatibility with `1.*`. 

Now `container_context` must be called with at least 1 argument or keyword-argument.
Thus if you want to reset the context for all containers you need to provide them explicity:
```python
async with container_context(MyContainer_1, MyContainer_2, ...):
    ...
```

---

### **`container_context()`** no longer accepts `reset_all_containers` keyword argument.

You can no longer reset the context for all containers by using the `container_context` context manager.
Previously you could have done something like this:
```python
async with container_context(reset_all_containers=True):
    # all ContextResources have been reset
    ...
```
Now you will need to explicitly pass the containers to the container context:
```python
async with container_context(MyContainer_1, MyContainer_2, ...):
    ...
```

---

### **`@inject(scope=...)`** no longer enters the scope.

The `@inject` decorator no longer enters the scope specified in the `scope` argument.

In `2.*` the provided scope would be entered before the function was called:
```python
@inject(scope=ContextScopes.REQUEST)
def injected(...): ...
    assert get_current_scope() == ContextScopes.REQUEST
```

In `3.*` the scope is only used to resolve relevant dependencies that match the provided scope.
Thus, to achieve the same behaviour as in `2.*` you need to use the `@container_context` context manager:
```python   
@container_context(scope=ContextScope.REQUEST)
@inject(scope=None) # None is required for exactly the same behaviour as in 2.*
def injected(...): ...
    assert get_current_scope() == ContextScopes.REQUEST
```
This fix will work, but is not recommended, please refer to the [scopes documentation](../introduction/scopes.md#named-scopes-with-the-inject-wrapper)

---

## Changes in the API

### Changes to naming of methods.

You can expect the default implementation of provider and container methods to be async.
This means that methods **not** explicitly ending with `_sync` are normally async.

This has also introduced new interfaces for operations where part of the implementation was missing (only async or sync implementation was provided in `2.*`).


For example `tear_down()` is now async per default and `tear_down_sync()` has been introduced.

Other examples of similar changes include:

 - `.async_resolve()` -> `.resolve()`
 - `.sync_resolve()` -> `.resolve_sync()`

---

### Tear down propagation enabled per default.

Tear down is now propagated to all dependencies by default.

Previously if you called tear down for a provider this only reset the given provider.
```python
await provider.tear_down()
```
In order to maintain this behaviour in `3.*`:
```python
await provider.tear_down(propagate=False)
```
For more details regarding tear-down propagation see the [documentation](../introduction/tear-down.md).

---

### Overriding is now async per default.

As mentioned [above](#changes-to-naming-of-methods), `.override()` methods are now async per default.

Thus any instances of the following in `2.*`:
```python
provider.override(value)
```
Should be changed to the following in `3.*`:
```python
provider.override_sync(value)
```

This is also the case for the following methods:

- `.override_context()` -> `.override_context_sync()`
- `.reset_override()` -> `.reset_override_sync()`

> **Note:** Overrides now support tear-down, read more in the [documentation](../testing/provider-overriding.md)

---

## Further Help

If you continue to experience issues during migration, consider creating a [discussion](https://github.com/modern-python/that-depends/discussions) or opening an [issue](https://github.com/modern-python/that-depends/issues).
