ivankorobkov / python-inject

Python dependency injection
Apache License 2.0
671 stars 77 forks source link

"Injecting" vs patching of built-ins/libraries #101

Open nskmda opened 2 months ago

nskmda commented 2 months ago

Team,

Is there any way to 'inject' a class attribute when a 'static' reference is used for the dependency.

Example:

class Configuration:
    _BUCKET_NAME = os.getenv('BUCKET', '')
    _USERS_TABLE_NAME = os.getenv('USERS_TABLE', None)
    _S3_BUCKET = boto3.client('s3')
    _DYNAMO_DB = boto3.resource('dynamodb')

    def bucketName(self):
         return self._BUCKET_NAME

    # other code in the class follows

I would like to have these values overridden in a test spec. so far i have to 'manually' override these values in the spec:

@inject.params(config=Configuration)
def feature_constructedWithMocks(config=None):
   config._BUCKET_NAME = injectedBucketMock
   assert injectedBucketMock == config.bucketName()

which would be nice to do 'automagically'.

ivankorobkov commented 2 months ago

Hi,

I would make it this way:

class Configuration:
  @classmethod
  def env(cls):
    c := cls()
    c.bucket_name = os.getenv('BUCKET', c.bucket_name)
    ...
    return c

  @classmethod
  def test(cls):
    ...

  def __init__(self):
    self.bucket_name = 'bucket'
    self.users_table_name = 'users'
    self.s3_bucket = 'mybucket'

And later I would bind Configuration manually to different providers in inject, i.e. to Configuration.env and to Configuration.test.

nskmda commented 2 months ago

I would make it this way:

Provided how [poorly] I can understand 'this way' 😃 it mostly means replacement of lib/built-ins is not really supported. Which is fine.

Now, just to confirm (since I haven't used it just yet): inject supports 'custom factories', right?

I'm thinking about having a custom factory now which will do the boto3.client and map it to some text-based name/key in the injector and then simply inject it. This way I should be able to inject those instances into my class constructor and re-map them in the feature spec to verify behavior, right?