Skip to content

Commit

Permalink
Rename and slightly redesign the library, v2 🚀
Browse files Browse the repository at this point in the history
  • Loading branch information
RobertoPrevato authored Dec 27, 2022
1 parent 778f956 commit c514d3d
Show file tree
Hide file tree
Showing 23 changed files with 585 additions and 320 deletions.
38 changes: 27 additions & 11 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ on:
- "*"

env:
PROJECT_NAME: rodi
PROJECT_NAME: neoteroi

jobs:
build:
Expand Down Expand Up @@ -52,8 +52,13 @@ jobs:
- name: Run tests
run: |
pip install -e .
pytest --doctest-modules --junitxml=junit/pytest-results-${{ matrix.python-version }}.xml --cov=$PROJECT_NAME --cov-report=xml tests/
- name: Test examples
run: |
for f in ./examples/*.py; do echo "Processing $f file..." && python $f; done
- name: Run linters
run: |
echo "Running linters - if build fails here, please be patient!"
Expand Down Expand Up @@ -102,15 +107,26 @@ jobs:
with:
name: dist
path: dist
- name: Publish distribution 📦 to Test PyPI
uses: pypa/gh-action-pypi-publish@master

- name: Use Python 3.11
uses: actions/setup-python@v1
with:
skip_existing: true
user: __token__
password: ${{ secrets.test_pypi_password }}
repository_url: https://test.pypi.org/legacy/
python-version: '3.11'

- name: Install dependencies
run: |
pip install twine
- name: Publish distribution 📦 to Test PyPI
run: |
twine upload -r testpypi dist/*
env:
TWINE_USERNAME: __token__
TWINE_PASSWORD: ${{ secrets.test_pypi_password2 }}

- name: Publish distribution 📦 to PyPI
uses: pypa/gh-action-pypi-publish@master
with:
user: __token__
password: ${{ secrets.pypi_password }}
run: |
twine upload -r pypi dist/*
env:
TWINE_USERNAME: __token__
TWINE_PASSWORD: ${{ secrets.pypi_password2 }}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ __pycache__
.mypy_cache
build
dist
deps
*.py,cover
1 change: 0 additions & 1 deletion .python-version

This file was deleted.

39 changes: 26 additions & 13 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,36 +5,49 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.1.4] - ??? :star:
- Replaces `setup.py` with `pyproject.toml`
## [2.0.0] - ??? :star:
- Introduces a `ContainerProtocol` to improve interoperability between
libraries and alternative implementations of DI containers. The protocol is
inspired by [punq](https://github.com/bobthemighty/punq), since its code API
is the most user-friendly and intelligible of those that were reviewed.
The `ContainerProtocol` can be used through [composition](https://en.wikipedia.org/wiki/Composition_over_inheritance)
to replace `rodi` with alternative implementations of dependency injection in
those libraries that use `DI`.
- Simplifies the code API of the library to support using the `Container` class
to `register` and `resolve` services. The class `Services` is still used and
available, but it's no more necessary to use it directly.
- Replaces `setup.py` with `pyproject.toml`.
- Renames context classes: "GetServiceContext" to "ActivationScope",
"ResolveContext" to "ResolutionContext".
- The "add_exact*" methods have been made private, to simplify the public API.

## [1.1.3] - 2022-03-27 :droplet:
- Corrects a bug that would cause false positives when raising exceptions
for circular dependencies. The code now let recursion errors happen if they
need to happen, re-raising a circular dependency error.

## [1.1.2] - 2022-03-14 :rabbit:
- Adds `py.typed` file
- Applies `isort` and enforces `isort` and `black` checks in CI pipeline
- Corrects the type annotation for `FactoryCallableType`
- Adds `py.typed` file.
- Applies `isort` and enforces `isort` and `black` checks in CI pipeline.
- Corrects the type annotation for `FactoryCallableType`.

## [1.1.1] - 2021-02-23 :cactus:
- Adds support for Generics and GenericAlias `Mapping[X, Y]`, `Iterable[T]`,
`List[T]`, `Set[T]`, `Tuple[T, ...]`, Python 3.9 `list[T]`, etc. ([fixes
#9](https://github.com/Neoteroi/rodi/issues/9))
#9](https://github.com/Neoteroi/rodi/issues/9)).
- Improves typing-friendliness making the `ServiceProvider.get` method
returning a value of the input type.
- Adds support for Python 3.10.0a5 ✨. However, when classes and functions
require locals, they need to be decorated. See [PEP
563](https://www.python.org/dev/peps/pep-0563/).

## [1.1.0] - 2021-01-31 :grapes:
- Adds support to resolve class attributes annotations
- Changes how classes without an `__init__` method are handled
- Updates links to the GitHub organization, [Neoteroi](https://github.com/Neoteroi)
- Adds support to resolve class attributes annotations.
- Changes how classes without an `__init__` method are handled.
- Updates links to the GitHub organization, [Neoteroi](https://github.com/Neoteroi).

## [1.0.9] - 2020-11-08 :octocat:
- Completely migrates to GitHub Workflows
- Improves build to test Python 3.6 and 3.9
- Adds a changelog
- Improves badges
- Completely migrates to GitHub Workflows.
- Improves build to test Python 3.6 and 3.9.
- Adds a changelog.
- Improves badges.
14 changes: 11 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ clean:


prepforbuild:
pip install --upgrade twine setuptools wheel
pip install build


build:
python -m build


test-release:
Expand All @@ -25,12 +29,16 @@ test:
flake8 && pytest


testcov:
pytest --cov-report html --cov-report annotate --cov=rodi tests/
test-cov:
pytest --cov-report html --cov=neoteroi tests/


format:
isort rodi
isort tests
black rodi
black tests


lint-types:
mypy neoteroi --explicit-package-bases
105 changes: 57 additions & 48 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,18 @@
![Build](https://github.com/Neoteroi/rodi/workflows/Build/badge.svg)
[![pypi](https://img.shields.io/pypi/v/rodi.svg)](https://pypi.python.org/pypi/rodi)
[![versions](https://img.shields.io/pypi/pyversions/rodi.svg)](https://github.com/Neoteroi/rodi)
[![codecov](https://codecov.io/gh/Neoteroi/rodi/branch/master/graph/badge.svg?token=VzAnusWIZt)](https://codecov.io/gh/Neoteroi/rodi)
[![license](https://img.shields.io/github/license/Neoteroi/rodi.svg)](https://github.com/Neoteroi/rodi/blob/master/LICENSE)
[![pypi](https://img.shields.io/pypi/v/neoteroi-di.svg)](https://pypi.python.org/pypi/neoteroi-di)
[![versions](https://img.shields.io/pypi/pyversions/neoteroi-di.svg)](https://github.com/Neoteroi/neoteroi-di)
[![codecov](https://codecov.io/gh/Neoteroi/rodi/branch/main/graph/badge.svg?token=VzAnusWIZt)](https://codecov.io/gh/Neoteroi/rodi)
[![license](https://img.shields.io/github/license/Neoteroi/rodi.svg)](https://github.com/Neoteroi/rodi/blob/main/LICENSE)

# Implementation of dependency injection for Python 3

**Features:**

* types resolution by signature types annotations (_type hints_)
* types resolution by class annotations (_type hints_) (new in version 1.1.0 :star:)
* types resolution by constructor parameter names and aliases (_convention over
configuration_)
* unintrusive: builds objects graph **without** the need to change classes
source code (unlike some other Python implementations of dependency
injection, like _[inject](https://pypi.org/project/Inject/)_)
* types resolution by class annotations (_type hints_)
* types resolution by names and aliases (_convention over configuration_)
* unintrusive: builds objects graph **without** the need to change the
source code of classes
* minimum overhead to obtain services, once the objects graph is built
* support for singletons, transient, and scoped services

Expand All @@ -24,65 +22,76 @@ Dependency injection in ASP.NET
Core](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-2.1),
[Using dependency injection in a .Net Core console
application](https://andrewlock.net/using-dependency-injection-in-a-net-core-console-application/)_).
The `ContainerProtocol` for v2 is inspired by [punq](https://github.com/bobthemighty/punq).

## Installation

```bash
pip install rodi
pip install neoteroi-di
```

## De gustibus non disputandum est
This library is designed to work both by type hints in constructor signatures,
and by constructor parameter names (convention over configuration), like
described in below diagram. It can be useful for those who like type hinting,
those who don't, and those who like having both options.
`neoteroi-di` is the new version of the library that was previously named
[`rodi`](https://pypi.org/project/rodi/). It is currently `alpha` and still
subject to change.

![Usage
option](https://raw.githubusercontent.com/Neoteroi/rodi/master/documentation/rodi-design-taste.png
"Usage option")
## Efficient

## Minimum overhead
`rodi` works by inspecting ____init____ methods **once** at
runtime, to generate functions that return instances of desired types.
Validation steps, for example to detect circular dependencies or missing
services, are done when building these functions, so additional validation is
not needed when activating services.
`neoteroi-di` works by inspecting code **once** at runtime, to generate
functions that return instances of desired types - as long as the object graph
is not altered. Inspections are done either on constructors
(____init____) or class annotations. Validation steps, for
example to detect circular dependencies or missing services, are done when
building these functions, so additional validation is not needed when
activating services.

For this reason, services are first registered inside an instance of
_Container_ class, which implements a method _build_provider()_ that
returns an instance of _Services_. The service provider is then used to obtain
desired services by type or name. Inspection and validation steps are done only
when creating an instance of service provider.
## Flexible

![Classes](https://raw.githubusercontent.com/Neoteroi/rodi/master/documentation/classes.png
"Classes")
`neoteroi-di` offers two code APIs:

In the example below, a singleton is registered by exact instance.
- one is kept as generic as possible, using a `ContainerProtocol` for scenarios
in which it is desirable being able to replace `neoteroi-di` with alternative
implementations of dependency injection for Python. The protocol only expects
a class being able to `register` and `resolve` types, and to tell if a type
is configured in it (`__contains__`). Even if other implementations of DI
don´t implement these three methods, it should be easy to use
[composition](https://en.wikipedia.org/wiki/Composition_over_inheritance) to
wrap other libraries with a compatible class.
- one is a more concrete implementation, following the previous implementation
in `rodi`, for scenarios where it's not desirable to consider alternative
implementations of dependency injection.

```python
container = Container()
container.add_instance(Cat("Celine"))
For this reason, the examples report two ways to achieve certain things.

services = container.build_provider() # --> validation, generation of functions
### Examples

cat = services.get(Cat)
For examples, refer to the [examples folder](./examples).

assert cat is not None
assert cat.name == "Celine"
```
### Recommended practices

All services should be configured once, when an application starts, and the
object graph should *not* be altered during normal program execution.
Example: if you build a web application, configure the object graph when
bootstrapping the application, avoid altering the `Container` configuration
while handling web requests.

Aim at keeping the `Container` and service graphs abstracted from the front-end
layer of your application, and avoid mixing runtime values with container
configuration. Example: if you build a web application, avoid if possible
relying on the HTTP Request object being a service registered in your container.

## Service life style:

* singleton - instantiated only once per service provider
* transient - services are instantiated every time they are required
* scoped - instantiated only once per service resolution (root call, e.g. once
per web request)
* scoped - instantiated only once per root service resolution call
(e.g. once per web request)

## Usage in BlackSheep
`rodi` is used in the [BlackSheep](https://www.neoteroi.dev/blacksheep/) web
framework to implement [dependency
injection](https://www.neoteroi.dev/blacksheep/dependency-injection/) for

`neoteroi-di` is used in the second version of [BlackSheep](https://www.neoteroi.dev/blacksheep/)
web framework to implement [dependency injection](https://www.neoteroi.dev/blacksheep/dependency-injection/) for
request handlers.

# Documentation
For documentation and examples, please refer to the [wiki in GitHub,
https://github.com/Neoteroi/rodi/wiki](https://github.com/Neoteroi/rodi/wiki).

Under construction. 🚧
Binary file removed documentation/classes.png
Binary file not shown.
45 changes: 0 additions & 45 deletions documentation/rodi-classes.wsd

This file was deleted.

Binary file removed documentation/rodi-design-taste.png
Binary file not shown.
21 changes: 0 additions & 21 deletions documentation/rodi.wsd

This file was deleted.

Loading

0 comments on commit c514d3d

Please sign in to comment.