An event-based framework
Project description
Streamline your Python Data Pipeline in one package
Building an all-in-one package that does api, scraping and integrates slow processing is hard.
aiosow
glues this all together.
Efficient, Event-Based, Modular
- Asynchronous
For efficient performance in I/O operations.
- Event-based
Helps you focus on the desired outcome rather than every single step.
- Modular
Allow each part of the codebase to be developed independently.
Enforced Separation of Concerns
aiosow
encourage a structure that separate implementations from the behavior.
Defined boundaries for different parts of the codebase, makes it easier to maintain and scale.
As a result, aiosow
can provide a set of tools that let developers interconnect and
customize multiple compositions with ease.
Customizable Asynchronous State-Machine
aiosow
is a programmable state-machine customizable with decorators and utilities for easy and efficient event triggering implementations.
State-machines provides a way to manage the flow of a system by storing and managing mutations in the memory
.
- One global
memory
represented as adict
- Mutations are the changes, just return a
dict
and you got a valid mutation - To bind this function in the state-machine you use
aiosow.bindings
These bindings allow to program the machine and express when to call the appropriate functions in the correct order, ensuring that the system behaves as expected.
The state machine is a useful tool for managing complex systems and ensuring that behavior is well-defined and predictable.
It allows to react to changes in the system and implement logic based on those changes (see aiosow.bindings.on
for instance).
aiosow.bindings
helps you express configurability, further data-piping, time-constraints, and more...aiosow.routines
helps you schedule code to flow trough timeaiosow.aiohttp
helps with HTTP requests
Compositions
compositions
are Python packages that use aiosow.bindings
to define aiosow
's behavior.
They follow a set of rules :
1. implementation
is strictly separate from the package's behavior
.
implementation
refers to the technical aspects of how a feature is coded and developed, such as algorithms and data structures. It focuses on the "how" of software development.
Separating these two concerns helps make software more maintainable and flexible over time.
Example of implementation
:
def get_data():
return 'foo'
def capitalize(s: str):
return s.capitalize()
2. Behaviors are defined in a behavior.py
file using aiosow.bindings
behavior
refers to the overall conduct and purpose of a software system, from the user's perspective. It focuses on the "what" and "when" of software development.
Example of behavior
:
signal_data_when, on_received_data_do = wire() # build a wire for data reception
data_treated_when, on_data_ready_do = wire() # build a wire for data that is ready
signal_data_when(get_data) # on data reception trigger functions registered
on_received_data_do(capitalize) # capitalize every data that is received
data_treated_when(capitalize) # once data is capitalized, send it
routine(1)(get_data) # get some data every second
on_data_ready_do(print) # anytime data is ready print it
3. Function prototypes use a strict naming consistency
aiosow.bindings.autofill
is the backone of the library used trough all tools to cast a function.
It is doing so by filling missing arguments from their prototype based on memory
.
Example:
`args` are poped first, `on` will pass the value that changed
|
| `foo` is autofilled, value is retrieved from `memory`
| |
v v
do_smtg = lambda ev, foo: print(ev, foo)
on('event')(do_smtg) # this will trigger do_smtg when 'bar' is set
setup(lambda : { 'foo': 'bar' }) # init 'foo' to 'bar'
setup(lambda : { 'event': 'trigger' }) # triggers `do_smtg`
> 'trigger', 'bar'
4. Implementation is low-coupled
Low coupled code refers to code that has minimal interdependencies between its different components or modules. In other words, changes to one component should not require extensive modifications to other components. This promotes modularity, maintainability, and flexibility in software development.
- When you write an
implementation
, you are providing vocabulary available to thebehavior
expression.
Example of high-coupled code:
def calculate_total_price(quantity, unit_price):
tax_rate = 0.1
subtotal = quantity * unit_price
tax_amount = subtotal * tax_rate
total_price = subtotal + tax_amount
return total_price
Example of low-coupled code:
def calculate_subtotal(quantity, unit_price):
subtotal = quantity * unit_price
return subtotal
def calculate_tax(subtotal):
tax_rate = 0.1
tax_amount = subtotal * tax_rate
return tax_amount
def calculate_total_price(quantity, unit_price):
subtotal = calculate_subtotal(quantity, unit_price)
tax_amount = calculate_tax(subtotal)
total_price = subtotal + tax_amount
return total_price
5. Initialization is decoupled of processing
The principle of decoupling the setup from operation involves separating the preparation from its actual use.
This ensures
- that changes or modifications made during setup do not interfere with the processing.
- that the implemented vocabulary focuses on expressing
how
.
6. Compositions should not be blocking the main execution
compositions
implementations should not contain synchronous code that blocks the main event loop, which could cause the entire application to freeze.
Instead, all I/O operations, heavy computations and blocking tasks should be executed asynchronously using the aiosow.bindings.make_async
decorator. This will ensure that the event loop remains responsive and can process other tasks efficiently.
Usage
aiosow <composition>
- You can run the
aiosow -h
to display all the possible options. - Options change based on the composition you are using
Using aiosow 'composition_name' -h
will load it's options and display them in
the help menu. Those are defined using the aiosow.options
decorator.
Installation
pip install aiosow
Contributing
We welcome contributions to this project from anyone. If you would like to contribute, please follow these guidelines:
- Fork the repository and create your branch from the latest main branch.
- Make your changes, and test them thoroughly.
- Submit a pull request describing your changes and explaining why they should be merged. Please ensure that your pull request adheres to our code of conduct.
- Wait for a maintainer to review your pull request, and make any requested changes.
- Once your pull request is approved, it will be merged into the main branch.
By contributing to this project, you agree to license your contribution under the project's license. If you have any questions or concerns, please reach out to us through GitHub issues.
License
This project is licensed under the MIT License
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.