From 2d55d388b1bcd61675befc9f55c2976d87961eaf Mon Sep 17 00:00:00 2001 From: Josh Orr Date: Sat, 19 Nov 2022 10:53:35 -0700 Subject: [PATCH] feat: rearranged docs to make them more clear. --- docs/{advanced.md => dependency-proxy.md} | 40 ++++------------------- docs/index.md | 10 +++--- docs/thread-safety.md | 40 +++++++++++++++++++++++ docs/unit-testing.md | 2 +- mkdocs.yml | 3 +- pyproject.toml | 2 +- 6 files changed, 55 insertions(+), 42 deletions(-) rename docs/{advanced.md => dependency-proxy.md} (55%) create mode 100644 docs/thread-safety.md diff --git a/docs/advanced.md b/docs/dependency-proxy.md similarity index 55% rename from docs/advanced.md rename to docs/dependency-proxy.md index 532c114..beca504 100644 --- a/docs/advanced.md +++ b/docs/dependency-proxy.md @@ -1,38 +1,10 @@ --- -title: REST API -description: Core utility +title: Dependency Proxy --- -## 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 `xinject.dependency.ThreadUnsafeResource` instead, -or by setting the **class attribute** (on your custom subclass of Dependency) -`xinject.dependency.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. - -Example for which you would want a separate dependency instance/object per-thread: - -- `requests` library session - - Requests libraries session object is not thread-safe, there is issue that's been around for 7 years - to make it thread safe that's still open. For now, you need a seperate requests Session per-thread. - - `requests-mock` also needs the session created after it's setup, so after unit test runs. -- boto client/dependency - - Library says it's not thread-safe, you need to use a different object per-thread. - - Moto mocking library for AWS services needs you to allocate a client after it's setup, - (so lazily allocate client/dependency from boto). - -## Active Dependency Proxy - -You can use the convenience method `xinject.dependency.Dependency.proxy` to easily get a -proxy object. +You can use the method +[`Dependency.proxy()`](api/xinject/dependency.html#xinject.dependency.Dependency.proxy){target=_blank} +to easily get a proxy object. All non-dunder attributes/methods will be grabbed/set on the current object instead of the proxy. @@ -83,7 +55,7 @@ if __name__ == '__main__': s3.resource.Bucket("my-bucket").download_file(file_name, dest_path) ``` -You can use the proxy objecy like a normal object. +You can use the proxy object like a normal object. The only things not forwarded are any method/attribute that starts with a `_`, which conveays the attribute as private/internal. @@ -95,5 +67,5 @@ Only use the proxy object for normal attribute/properties/methods. If you need do use an attribute/method that starts with an underscore `_`, grab the current object directly via `S3.grab()`. -The [`grab`](api/xinject/dependency.html#xinject.dependency.Dependency.grab) +The [`grab()`](api/xinject/dependency.html#xinject.dependency.Dependency.grab){target=_blank} method returns the current real object each time it's called (and not a proxy). diff --git a/docs/index.md b/docs/index.md index d2608e9..92bd2a0 100644 --- a/docs/index.md +++ b/docs/index.md @@ -31,12 +31,12 @@ in an easy to understand and self-documenting way. documentation. When you're done here and want more details go to [API Reference](api/xinject) - or directly to [`Dependency API Refrence`](api/xinject/dependency.html#xinject.dependency.Dependency) + or directly to [`Dependency API Refrence`](api/xinject/dependency.html#xinject.dependency.Dependency){target=_blank} for more detailed reference-type documentation. ## Quick Start -Although it's not required, most of the time you'll want to subclass [`Dependency`](api/xinject/dependency.html#xinject.dependency.Dependency). +Although it's not required, most of the time you'll want to subclass [`Dependency`](api/xinject/dependency.html#xinject.dependency.Dependency){target=_blank}. The subclass will inherit some nice features that make it easier to use. The following is a specific usecase followed by a more generalized example @@ -50,7 +50,7 @@ We have a choice to inherit from ether Dependency, or DependencyPerThread. The normal `Dependency` class lets the dependency be shared between threads, so more of a true singleton type of object where under normal/default circomstances there would ever only be one instance of a partculare `Dependency`. -Using [`DependencyPerThread`](api/xinject/dependency.html#xinject.dependency.DependencyPerThread) will automatically get a +Using [`DependencyPerThread`](api/xinject/dependency.html#xinject.dependency.DependencyPerThread){target=_blank} will automatically get a separate dependency object per-thread (ie: separate instance per-thread). It simply inherits from Dependency and configures it to not be thread sharable. @@ -204,7 +204,7 @@ XContext.grab().add(s3_mocking_obj, for_type=S3) ### Generalized/Generic Example -Although it's not required, most of the time you'll want to subclass [`Dependency`](api/xinject/dependency.html#xinject.dependency.Dependency). +Although it's not required, most of the time you'll want to subclass [`Dependency`](api/xinject/dependency.html#xinject.dependency.Dependency){target=_blank}. The subclass will inherit some nice features that make it easier to use. ```python @@ -283,7 +283,7 @@ with MyUniversalDependency(name='injected-value'): ## Overview -The main class used most of the time is [`Dependency`](api/xinject/dependency.html#xinject.dependency.Dependency). +The main class used most of the time is [`Dependency`](api/xinject/dependency.html#xinject.dependency.Dependency){target=_blank}. Allows you to create sub-classes that act as sharable singleton-type objects that we are calling resources here. diff --git a/docs/thread-safety.md b/docs/thread-safety.md new file mode 100644 index 0000000..e82eb6e --- /dev/null +++ b/docs/thread-safety.md @@ -0,0 +1,40 @@ +--- +title: Thread Safety +--- + +There is a concept of an app-root, and thread-root [`XContext`](api/xinject/context.html#xinject.context.XContext){target=_blank}. +The app-root stores dependecies that can be shared between threads, whule the thread-root context stores dependence +for a specific thread. + +By default, each Dependency subclass can 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 `xinject.dependency.DependencyPerThread` instead, +or by setting the **class attribute** (on your subclass of Dependency) +`thread_sharable` to `False`; ie: + +```python +from xinject import Dependency, DependencyPerThread + +class MyThreadUnsafeDependencyOpt1(DependencyPerThread): + ... + +class MyThreadUnsafeDependencyOpt2(Dependency, thread_sharable=False): + ... +``` + + +Things that are probably not thread-safe in general +are resources that contain network/remote type connections/sessions/clients. + +Example for which you would want a separate dependency instance/object per-thread: + +- `requests` library session + - Requests libraries session object is not thread-safe, there is issue that's been around for 7 years + to make it thread safe that's still open. For now, you need a seperate requests Session per-thread. + - `requests-mock` also needs the session created after it's setup, so after unit test runs. +- boto client/dependency + - Library says it's not thread-safe, you need to use a different object per-thread. + - Moto mocking library for AWS services needs you to allocate a client after it's setup, + (so lazily allocate client/dependency from boto). diff --git a/docs/unit-testing.md b/docs/unit-testing.md index 03143e9..525bd5c 100644 --- a/docs/unit-testing.md +++ b/docs/unit-testing.md @@ -107,7 +107,7 @@ This means that by default, at the start of each running unit test function ther so as long as xinject is installed in the environment as a dependency it will find this and autoload this fixture for each unit test. - If curious The [`xinject_test_context`](api/xinject/pytest_plugin.html#xinject.pytest_plugin.xinject_test_context) + If curious The [`xinject_test_context`](api/xinject/pytest_plugin.html#xinject.pytest_plugin.xinject_test_context){target=_blank} fixture is how it's implemented. diff --git a/mkdocs.yml b/mkdocs.yml index 28bb501..d85d3a6 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -8,7 +8,8 @@ nav: - index.md - Unit Testing: unit-testing.md - Data Classes: dataclasses.md - - Advanced Use Cases: advanced.md + - thread-safety.md + - dependency-proxy.md - API Reference: api/xinject/" target="_blank - Changelog: changelog.md diff --git a/pyproject.toml b/pyproject.toml index fc3d42c..86a65e6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [tool.poetry] name = "xinject" version = "0.3.0" -description = "Inject universal resources/dependencies lazily." +description = "Lazy dependency injection." authors = ["Josh Orr "] packages = [{include = "xinject"}]