python-injector / python-injector/injector
Programming to an interface
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 1.5k
- Forks
- 94
- PR merge metrics
- No merged PRs in 30d
Description
First of all, thanks for the library. It's a very good to see python moving towards this practices.
I have been playing with this library in conjunction with flask-injector (https://pypi.org/project/Flask-Injector - and thanks also for that) for a couple of months now, and I believe there are some limitations, if we want to follow and applies 3 principles from SOLID in large or particularly complex code base. Particularly, if I want to respect Liskov Subistitution , Interface segregation, and dependency inversion.
Some conversation was carried on here: https://github.com/alecthomas/injector/issues/123
I thought about not opening this thread at all, as it appears from there that you are not necessarily going to support that kind of behaviour, but at the same time it's probably an healthy discussion to have as what is asked there it's a very good point, and ultimately will allow me (and others) to understand in which direction this library intends to go.
What do I want to do?
I want to define an interface (using an abstract class, implementing nothing in python via abc) that sets the same contract for all implementations to respect (using type hints and returns in order to be precise about the contract).
After that I want to be able to pass a particular implementation to the injection in the client code via composition, and possibly at construction time, using as type declaration the interface rather than the implementation.
eg.
class NewsFeedRetriever(ABC):
@abstractmethod
def get_news(self) -> NewsResults:
pass
----
class NewsResults:
def __init__(self, data: dict):
# validate data with some custom logic
self.__data = data
def get_data(self) -> dict:
return self.__data
----
class NewYorkTimesFeedRetriever(NewsFeedRetriever):
def __init__(self, url: str, user: str, password: str):
self.__url = url
self.__user = user
self.__password = password
def get_news(self) -> NewsResults:
# use url user and password to get the news
data = {"news": "from new_york_times"}
return NewsResults(data)
-----
class TheGuardianFeedRetriever(NewsFeedRetriever):
def __init__(self, url):
# In this case I don't need user and pass
self.__url = url
def get_news(self) -> NewsResults:
# but I still implement the contract and return NewsResults
data = {"news": "from the guardian"}
return NewsResults(data)
----
class WashingtonPostFeedRetriever(NewsFeedRetriever):
def __init__(self, url):
# In this case I don't need user and pass
self.__url = url
def get_news(self) -> NewsResults:
# but I still implement the contract and return NewsResults
data = {"news": "from washington post"}
return NewsResults(data)
----
class NewsFeedAggregator:
def __init__(self, news_feeder1: NewsFeedRetriever, news_feeder2: NewsFeedRetriever):
self.__news_feeder1 = news_feeder1
self.__news_feeder2 = news_feeder2
def get_news(self) -> [NewsResults]:
return [self.__news_feeder1.get_news(), self.__news_feeder2.get_news()]
app.py
news_feeder_1 = NewYorkTimesFeedRetriever('https://www.nytimes.com/', 'someuser', 'somepass')
news_feeder_2 = WashingtonPostFeedRetriever('https://www.washingtonpost.com/')
news_feed_aggregator = NewsFeedAggregator(
news_feeder_1,
news_feeder_2
)
all_my_news = news_feed_aggregator.get_news()
After some months you get bored with the Washington post and you replace it with the guardian. All you need to do is
news_feeder_2 = TheGuardianFeedRetriever('https://www.theguardian.com/')
If I use bind I can always only bind one implementation!
What is the best way to achieve this with Injector ?
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
The issue does not name repository files or tests. Start by reviewing the Injector.bind entry point and the discussion in issue #123 to understand the current single-implementation behavior. Done would require a decided, supported approach for injecting multiple interface implementations, with its expected usage and tests agreed by the maintainers.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- tooling
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 20/100