Skip to content

Commit

Permalink
docs: splitting up docs into multiple files.
Browse files Browse the repository at this point in the history
  • Loading branch information
joshorr committed Nov 2, 2022
1 parent 4cb318b commit 71fee52
Show file tree
Hide file tree
Showing 4 changed files with 220 additions and 186 deletions.
89 changes: 89 additions & 0 deletions docs/advanced.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@


## Thread Safety

There is a concept of an app-root, and thread-root contexts.

By default, each Dependency subclass will be shared between different threads,
ie: it's assumed to be thread-safe.

You can indicate a Dependency subclass should not be shared between threads
by inheriting from `udepend.resource.ThreadUnsafeResource` instead,
or by setting the **class attribute** (on your custom sub-class of Dependency)
`udepend.resource.Dependency.resource_thread_sharable` to `False`.

Things that are probably not thread-safe in general
are resources that contain network/remote type connections/sessions/clients.

Concrete Examples In Code Base:

- Requests library session
- xyn_model_rest uses a Dependency to wrap requests library session, so it can automatically
reuse connections on same thread, but use new session if on different thread.
Also helps with unit testing, when Mocking requests URL calls.
- boto client/resource
- Library says it's not thread-safe, you need to use a diffrent object per-thread.
- Moto mocking library for AWS services needs you to allocate a client after it's setup,
(so lazily allocate client/resource from boto).
- Use `xyn_aws` for easy to use Dependency's that wrap boto client/resources that
accomplish being both lazy and will allocate a new one per-thread for you automatically.

## Active Dependency Proxy

You can use the convenience method `udepend.resource.Dependency.resource_proxy` to easily get a
proxy object.

Or you can use `udepend.proxy.ActiveResourceProxy.wrap` to create an object that will act
like the current resource.
All non-dunder attributes/methods will be grabbed/set on the current object instead of the proxy.

This means you can call all non-special methods and access normal attributes,
as if the object was really the currently active resource instance.

Any methods/attributes that start with a `_` will not be used on the proxied object,
but will be used on only the proxy-object it's self.
This means, you should not ask/set any attributes that start with `_` (underscore)
when using the proxy object.

A real-world example is `xyn_config.config.config`, it uses this code for that object:

```python
from udepend import ActiveResourceProxy
from xyn_config import Config

# The `xny_resource.proxy.ActiveResourceProxy.wrap` method to get a correctly type-hinted (for IDE)
# proxy back:
config = ActiveResourceProxy.wrap(Config)

# This is a simpler way to get the same proxy
# (no imports are needed, just call the class method on any resource class):
config = Config.resource_proxy()
```

Now someone can import and use it as-if it's the current config object:

```python
from xyn_config import config

value = config.get('some_config_var')
```

When you ask `config` for it's `get` attribute, it will get it from the current
active resource for `Config`. So it's the equivalent of doing this:

```python
from xyn_config import Config

get_method = Config.resource().get
value = get_method('some_config_var')
```

The code then executes the method that was attached to the `get` attribute.
This makes the call-stack clean, if an error happens it won't be going through
the ActiveResourceProxy.
The `udepend.proxy.ActiveResourceProxy` already return the `get` method and is finished.
The outer-code is the one that executed/called the method.

Another read-world example is in the `xyn_aws`.

See `udepend.proxy.ActiveResourceProxy` for more ref-doc type details.
39 changes: 39 additions & 0 deletions docs/dataclasses.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
## Dependency + Dataclasses

You can use the built-in dataclasses with Dependency without a problem.
Just ensure all fields are optional (ie: they all have default values),
so they are not required when creating/init'ing the object.

You can also provide a `__post_init__` method on your `Dependency`
subclass to help you initialize the values into a good default state
(a standard feature of dataclasses).

The purpose to enable lazily creation of the Dependency object
the very first time it's asked for.

Example:

```python
from udepend import Dependency
from dataclasses import dataclass


@dataclass
class DataResource(Dependency):
# Making all fields optional, so DataResource can be created lazily:
my_optional_field: str = None
another_optional_field: str = "hello!"


# Get current DataResource resource, print it's another_optional_field;
# will print out `hello!`:
print(DataResource.resource().another_optional_field)


DataResource.depend()

DataResource.dependency()

DataResource.resource()

```
Loading

0 comments on commit 71fee52

Please sign in to comment.