Module dynamo_pydantic.settings
Global variables
var dyn_settings-
Proxy to the current
DynSettingscurrently used/injected at the current moment. Used this object use like you would use normal instance of DynSettings.
Classes
class DynSettings (*,
consistent_reads: bool | DefaultType = Default)-
Expand source code
class DynSettings(Dependency): def __init__(self, *, consistent_reads: bool | DefaultType = Default): self.consistent_reads = consistent_reads consistent_reads: bool | DefaultType = Default """ Way to change what the default consistent read value should be, If set it will be used over the default value for the model (but won't override True/False passed directly to client as method paramter to get/scan/etc. """ default_prefix_generator: 'Callable[[DynObjManager], str] | None '= None create_tables_if_needed: bool = False """ If set to `True`, will create tables lazily if needed on first get/put/delete/save/etc. """If you have not already done so, you should also read the xinject project's README.md for an overview of the library before diving into the below text, that's more of like reference material.
Summary
Allows you to create subclasses that act as sharable dependencies. Things that should stick around and should be created lazily.
Also allows code to temporarily create, customize and activate a dependency if you don't want the customization to stick around permanently. You can do it without your or other code needing to be aware of each other.
This helps promote code decoupling, since it's so easy to make a Resource activate it as the 'current' version to use.
The only coupling that takes place is to the Resource sub-class it's self.
You can also easily have each thread lazily create seperate instance of your Resource, by inheriting from
DependencyPerThread.Each separate piece of code that uses a particular Resource subclass can be completely unaware of each other, and yet each one can take advantage of the shared dependency.
This means that Resource can help cover dependency-injection use-cases.
Overview
A
Resourcerepresents an object in aXContext. Generally, dependencies that are added/created inside aXContextinherit from this abstract baseResourceclass, but are not required too.Resourcejust adds some class-level conveince methods and configuratino options. Inheriting from Resource also helps self-document that it's a Resource.See Resources at top of this module for a general overview of how dependencies and
XContext's work. You should also read the xinject project's README.md for a high-level overview. The text below is more like plain refrence matrial.Get the current dependency via
Resource.dependency, you can call it on sub-class/concreate dependency type, like so:>>> from xinject import Dependency >>> class MyConfig(Dependency): ... some_setting: str = "default-setting-string" >>> >>> MyConfig.grab().some_settingBy default, Resource's act like a singletons; in that child contexs will simply get the same instance of the dependency that the parent context has.
If you inherit from this class, when you have
Resource.dependencycalled on you, we will do our best to ensure that the same object instance is returned every time (there are two exceptions, keep reading).These dependencies are stored in the current
XContext's parent. What happens is:If the current
XContextand none of their parents have this object and it's asked for (like what happens whenResource.dependencyis called on it), it will be created in the deepest/oldest parent XContext.This is the first parent
XContextwho'sXContext.parentis None. That way it should be visible to everyone on the current thread since it will normally be created in the app-rootXContext.If the Dependency can't be shared between multiple threads, creation would normally happen at the thread-root XContext instead of the app-root one.
If we don't already exist in any parent, then we must be created the first time we are asked for. Normally it will simply be a direct call the dependency-type being requested, this is the normal way to create objects in python:
>>> class MyResource(Dependency): >>> pass >>> >>> MyResource.grab()When that last line is executed, and the current or any parent context has a
MyResourcedependency;XContextwill simply create one via calling the dependency type:>>> MyResource()You can allocate the dependency yourself with custom options and add it to the XContext your self.
Here are the various ways to do that, via:
-
XContext.addAdds dependency to a specific XContext that already exists (or replaces if one has already been directly added in the past to that specific Context). When/While XContext is active, these added dependencies will be thecurrentones. -
Decorator, ie:
@MyResource()>>> from xny_config import Config, config >>> >>> @DependencySubclass(service="override-service-name") >>> def my_method(): >>> assert config.service == "override-service-name" -
via a with statement.
>>> def my_method(): >>> with @DependencySubclass(service="override-service-name") >>> assert config.service == "override-service-name" -
multiple in single statement by making your own XContext directly:
>>> def my_method(): >>> with @XContext([ >>> DependencySubclass(service="override-service-name"), >>> SomeOtherDep(name='new-name') >>> ]): >>> assert config.service == "override-service-name"
Background on Unit Testing
By default, unit tests always create a new blank
XContextwithparent=None. THis is done by an autouse fixture (xinject_test_context()) THis forces every unit test run to create new dependencies when they are asked for (lazily).This fixture is used automatically for each unit test, it clears the app-root XContext, removes all current thread-root XContext's and their children from being
active. just beofre each run of a unit test.That way it will recreate any shared dependency each time and a unit test can't leak dependencies it added or changed into the next run.
One example of why this is good is for
motowhen mocking dynamodb in boto3 client. Can use dependency to ensure that we get a new dynamodb shared dependency forbotoeach time a unit test executes (which helps withmoto, it needs to be active when a dependency is allocated/used).This is exactly what we want for each unit test run, to have a blank-slate for all the vairous dependencies.
If a particulre set of unit-tests need to have specific dependcies, you can use fixtures to modify/add various dependcies as needed for each indivirual unit-test function run.
When the application runs for real though, we do generally want to use the dependencies in a shared fashion. So normally we only allocate a new blank-root
@XContext(parent=None)either at the start of a normal application run, or during a unit-test.Ancestors
Class variables
var consistent_reads : bool | DefaultType-
Way to change what the default consistent read value should be, If set it will be used over the default value for the model (but won't override True/False passed directly to client as method paramter to get/scan/etc.
var create_tables_if_needed : bool-
If set to
True, will create tables lazily if needed on first get/put/delete/save/etc. var default_prefix_generator : Callable[[DynObjManager], str] | None-
The type of the None singleton.
Static methods
def __init_subclass__(thread_sharable: bool | DefaultType = Default,
remove_between_unittests: bool | DefaultType = Default,
attributes_to_skip_while_copying: Iterable[str] | None = Default,
**kwargs)-
Inherited from:
Dependency.__init_subclass__Args
remove_between_unittests- If
False(default): Dependency will be removed from global context before/after as each individual …
def grab() ‑> ~T-
Inherited from:
Dependency.grabGets a potentially shared dependency from the current
udpend.context.XContext… def proxy() ‑> ~R-
Inherited from:
Dependency.proxyReturns a proxy-object, that when and attribute is asked for, it will proxy it to the current object of
cls… def proxy_attribute(attribute_name: str) ‑> Any-
Inherited from:
Dependency.proxy_attributeReturns a proxy-object, that when and attribute is asked for, it will proxy it to the current attribute value on the current object of
cls…
Instance variables
var obj-
Inherited from:
Dependency.objclass property/attribute that will return the current dependency for the subclass it's asked on by calling
Dependency.grab, passing no extra …
Methods
def __call__(self, func)-
Inherited from:
Dependency.__call__This makes Resource subclasses have an ability to be used as function decorators by default unless this method is overriden to provide some other …
def __copy__(self)-
Inherited from:
Dependency.__copy__Basic shallow copy protection (I am wondering if I should just remove this default copy code) …
-