fennflow 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fennflow-0.1.0/.github/ISSUE_TEMPLATE/bug-notice.yaml +74 -0
- fennflow-0.1.0/.github/ISSUE_TEMPLATE/engineering-task.yaml +49 -0
- fennflow-0.1.0/.github/ISSUE_TEMPLATE/feature-request.yaml +30 -0
- fennflow-0.1.0/.github/ISSUE_TEMPLATE/question.yaml +35 -0
- fennflow-0.1.0/.github/dependabot.yml +12 -0
- fennflow-0.1.0/.github/pull_request_template.md +6 -0
- fennflow-0.1.0/.github/release.yml +19 -0
- fennflow-0.1.0/.github/workflows/coverage-report.yml +81 -0
- fennflow-0.1.0/.github/workflows/publish.yml +34 -0
- fennflow-0.1.0/.gitignore +16 -0
- fennflow-0.1.0/.python-version +1 -0
- fennflow-0.1.0/LICENSE +21 -0
- fennflow-0.1.0/PKG-INFO +162 -0
- fennflow-0.1.0/README.md +132 -0
- fennflow-0.1.0/mkdocs.yml +16 -0
- fennflow-0.1.0/pyproject.toml +144 -0
- fennflow-0.1.0/src/fennflow/__init__.py +7 -0
- fennflow-0.1.0/src/fennflow/_base_exception.py +2 -0
- fennflow-0.1.0/src/fennflow/_datetime.py +10 -0
- fennflow-0.1.0/src/fennflow/_decorators.py +33 -0
- fennflow-0.1.0/src/fennflow/_new_types.py +7 -0
- fennflow-0.1.0/src/fennflow/_operations/__init__.py +0 -0
- fennflow-0.1.0/src/fennflow/_operations/context/__init__.py +0 -0
- fennflow-0.1.0/src/fennflow/_operations/context/abstract.py +6 -0
- fennflow-0.1.0/src/fennflow/_operations/context/delete.py +15 -0
- fennflow-0.1.0/src/fennflow/_operations/context/put.py +14 -0
- fennflow-0.1.0/src/fennflow/_operations/context/types.py +7 -0
- fennflow-0.1.0/src/fennflow/_operations/dto.py +79 -0
- fennflow-0.1.0/src/fennflow/_operations/enums.py +19 -0
- fennflow-0.1.0/src/fennflow/_operations/executor.py +47 -0
- fennflow-0.1.0/src/fennflow/_operations/flows/__init__.py +0 -0
- fennflow-0.1.0/src/fennflow/_operations/flows/abstract.py +33 -0
- fennflow-0.1.0/src/fennflow/_operations/flows/delete.py +69 -0
- fennflow-0.1.0/src/fennflow/_operations/flows/put.py +52 -0
- fennflow-0.1.0/src/fennflow/_operations/registry.py +15 -0
- fennflow-0.1.0/src/fennflow/_path.py +31 -0
- fennflow-0.1.0/src/fennflow/_reconciler/__init__.py +10 -0
- fennflow-0.1.0/src/fennflow/_reconciler/_orchestrator.py +86 -0
- fennflow-0.1.0/src/fennflow/_reconciler/config.py +10 -0
- fennflow-0.1.0/src/fennflow/_reconciler/core.py +112 -0
- fennflow-0.1.0/src/fennflow/_reconciler/enums.py +29 -0
- fennflow-0.1.0/src/fennflow/_reconciler/exceptions.py +6 -0
- fennflow-0.1.0/src/fennflow/_resolver.py +62 -0
- fennflow-0.1.0/src/fennflow/_sentinel.py +69 -0
- fennflow-0.1.0/src/fennflow/backends/__init__.py +7 -0
- fennflow-0.1.0/src/fennflow/backends/abstract/__init__.py +0 -0
- fennflow-0.1.0/src/fennflow/backends/abstract/annotations.py +30 -0
- fennflow-0.1.0/src/fennflow/backends/abstract/base.py +95 -0
- fennflow-0.1.0/src/fennflow/backends/abstract/config.py +15 -0
- fennflow-0.1.0/src/fennflow/backends/enums.py +9 -0
- fennflow-0.1.0/src/fennflow/backends/exceptions.py +9 -0
- fennflow-0.1.0/src/fennflow/backends/factory.py +22 -0
- fennflow-0.1.0/src/fennflow/backends/in_memory/__init__.py +7 -0
- fennflow-0.1.0/src/fennflow/backends/in_memory/_select.py +124 -0
- fennflow-0.1.0/src/fennflow/backends/in_memory/base.py +214 -0
- fennflow-0.1.0/src/fennflow/backends/in_memory/config.py +9 -0
- fennflow-0.1.0/src/fennflow/backends/registry.py +5 -0
- fennflow-0.1.0/src/fennflow/backends/responses.py +33 -0
- fennflow-0.1.0/src/fennflow/backends/types/__init__.py +0 -0
- fennflow-0.1.0/src/fennflow/backends/types/config.py +5 -0
- fennflow-0.1.0/src/fennflow/connectors/__init__.py +11 -0
- fennflow-0.1.0/src/fennflow/connectors/abstract/__init__.py +3 -0
- fennflow-0.1.0/src/fennflow/connectors/abstract/base.py +88 -0
- fennflow-0.1.0/src/fennflow/connectors/factory.py +40 -0
- fennflow-0.1.0/src/fennflow/connectors/in_memory/__init__.py +7 -0
- fennflow-0.1.0/src/fennflow/connectors/in_memory/config.py +9 -0
- fennflow-0.1.0/src/fennflow/connectors/in_memory/connector.py +152 -0
- fennflow-0.1.0/src/fennflow/connectors/registry.py +9 -0
- fennflow-0.1.0/src/fennflow/connectors/s3/__init__.py +7 -0
- fennflow-0.1.0/src/fennflow/connectors/s3/client.py +76 -0
- fennflow-0.1.0/src/fennflow/connectors/s3/config.py +43 -0
- fennflow-0.1.0/src/fennflow/connectors/s3/connector.py +182 -0
- fennflow-0.1.0/src/fennflow/connectors/types/__init__.py +0 -0
- fennflow-0.1.0/src/fennflow/connectors/types/config.py +6 -0
- fennflow-0.1.0/src/fennflow/core/__init__.py +5 -0
- fennflow-0.1.0/src/fennflow/core/configs/__init__.py +0 -0
- fennflow-0.1.0/src/fennflow/core/configs/base.py +28 -0
- fennflow-0.1.0/src/fennflow/exceptions/__init__.py +28 -0
- fennflow-0.1.0/src/fennflow/files/__init__.py +23 -0
- fennflow-0.1.0/src/fennflow/files/exceptions/__init__.py +18 -0
- fennflow-0.1.0/src/fennflow/files/exceptions/base.py +5 -0
- fennflow-0.1.0/src/fennflow/files/exceptions/cannot_parse_extension.py +13 -0
- fennflow-0.1.0/src/fennflow/files/exceptions/extension_cannot_be_guessed.py +5 -0
- fennflow-0.1.0/src/fennflow/files/exceptions/filename_and_mediatype_both_none.py +12 -0
- fennflow-0.1.0/src/fennflow/files/exceptions/filename_is_none.py +8 -0
- fennflow-0.1.0/src/fennflow/files/exceptions/media_type_cannot_be_guessed.py +23 -0
- fennflow-0.1.0/src/fennflow/files/exceptions/storage_prefix_is_none.py +8 -0
- fennflow-0.1.0/src/fennflow/files/factory.py +115 -0
- fennflow-0.1.0/src/fennflow/files/media/__init__.py +21 -0
- fennflow-0.1.0/src/fennflow/files/media/abstract/__init__.py +0 -0
- fennflow-0.1.0/src/fennflow/files/media/abstract/content.py +21 -0
- fennflow-0.1.0/src/fennflow/files/media/abstract/from_content.py +41 -0
- fennflow-0.1.0/src/fennflow/files/media/audio_content.py +11 -0
- fennflow-0.1.0/src/fennflow/files/media/base.py +163 -0
- fennflow-0.1.0/src/fennflow/files/media/binary_content.py +32 -0
- fennflow-0.1.0/src/fennflow/files/media/image_content.py +13 -0
- fennflow-0.1.0/src/fennflow/files/media/json_content.py +71 -0
- fennflow-0.1.0/src/fennflow/files/media/pdf_content.py +5 -0
- fennflow-0.1.0/src/fennflow/files/media/text_content.py +62 -0
- fennflow-0.1.0/src/fennflow/files/media/url_content.py +11 -0
- fennflow-0.1.0/src/fennflow/files/media/video_content.py +15 -0
- fennflow-0.1.0/src/fennflow/files/protocols/__init__.py +0 -0
- fennflow-0.1.0/src/fennflow/files/protocols/has_data.py +15 -0
- fennflow-0.1.0/src/fennflow/files/protocols/has_data_and_ext.py +20 -0
- fennflow-0.1.0/src/fennflow/files/protocols/has_data_and_ext_obj.py +19 -0
- fennflow-0.1.0/src/fennflow/files/protocols/has_ext.py +20 -0
- fennflow-0.1.0/src/fennflow/files/registry.py +21 -0
- fennflow-0.1.0/src/fennflow/files/responses/__init__.py +0 -0
- fennflow-0.1.0/src/fennflow/files/responses/base.py +127 -0
- fennflow-0.1.0/src/fennflow/files/responses/list.py +45 -0
- fennflow-0.1.0/src/fennflow/files/types.py +26 -0
- fennflow-0.1.0/src/fennflow/py.typed +0 -0
- fennflow-0.1.0/src/fennflow/repositories/__init__.py +14 -0
- fennflow-0.1.0/src/fennflow/repositories/at.py +60 -0
- fennflow-0.1.0/src/fennflow/repositories/base.py +30 -0
- fennflow-0.1.0/src/fennflow/repositories/delete.py +59 -0
- fennflow-0.1.0/src/fennflow/repositories/fields/__init__.py +7 -0
- fennflow-0.1.0/src/fennflow/repositories/fields/base.py +81 -0
- fennflow-0.1.0/src/fennflow/repositories/fields/s3.py +44 -0
- fennflow-0.1.0/src/fennflow/repositories/get.py +73 -0
- fennflow-0.1.0/src/fennflow/repositories/list.py +57 -0
- fennflow-0.1.0/src/fennflow/repositories/put.py +70 -0
- fennflow-0.1.0/src/fennflow/uow/__init__.py +5 -0
- fennflow-0.1.0/src/fennflow/uow/core.py +209 -0
- fennflow-0.1.0/src/fennflow/uow/inspector.py +23 -0
- fennflow-0.1.0/uv.lock +2180 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
name: 🐛 fennflow Bug
|
|
2
|
+
description: Report a bug or unexpected behavior in fennflow
|
|
3
|
+
labels: ["bug"]
|
|
4
|
+
assignees: ["Alex-FIR-IT"]
|
|
5
|
+
|
|
6
|
+
body:
|
|
7
|
+
- type: markdown
|
|
8
|
+
attributes:
|
|
9
|
+
value: |
|
|
10
|
+
Thank you for contributing to fennflow! ✊
|
|
11
|
+
|
|
12
|
+
Please provide as much information as possible to help us reproduce and fix your issue quickly.
|
|
13
|
+
|
|
14
|
+
- type: checkboxes
|
|
15
|
+
id: checks
|
|
16
|
+
attributes:
|
|
17
|
+
label: Initial Checks
|
|
18
|
+
description: Just making sure we're on the same page.
|
|
19
|
+
options:
|
|
20
|
+
- label: I'm using the [latest version](https://github.com/Alex-FIR-IT/fennflow/releases/latest) of fennflow
|
|
21
|
+
required: true
|
|
22
|
+
- label: I've searched for my issue in [the issue tracker](https://github.com/Alex-FIR-IT/fennflow/issues) before opening this issue
|
|
23
|
+
required: true
|
|
24
|
+
|
|
25
|
+
- type: textarea
|
|
26
|
+
id: description
|
|
27
|
+
attributes:
|
|
28
|
+
label: Description
|
|
29
|
+
description: |
|
|
30
|
+
Please explain what you're seeing and what you would expect to see.
|
|
31
|
+
|
|
32
|
+
- **A line of code is worth a thousand words**: you can include minimal, reproducible example code below.
|
|
33
|
+
validations:
|
|
34
|
+
required: true
|
|
35
|
+
|
|
36
|
+
- type: textarea
|
|
37
|
+
id: stacktrace
|
|
38
|
+
attributes:
|
|
39
|
+
label: Stacktrace
|
|
40
|
+
description: |
|
|
41
|
+
Please provide the full stacktrace of any error you're encountering.
|
|
42
|
+
validations:
|
|
43
|
+
required: false
|
|
44
|
+
|
|
45
|
+
- type: textarea
|
|
46
|
+
id: example
|
|
47
|
+
attributes:
|
|
48
|
+
label: Minimal, Reproducible Example
|
|
49
|
+
description: >
|
|
50
|
+
If at all possible, please add a self-contained,
|
|
51
|
+
[minimal, reproducible example](https://stackoverflow.com/help/minimal-reproducible-example)
|
|
52
|
+
that demonstrates the bug.
|
|
53
|
+
|
|
54
|
+
(This will be automatically formatted as code, so no need for backticks.)
|
|
55
|
+
|
|
56
|
+
placeholder: |
|
|
57
|
+
from fennflow import ...
|
|
58
|
+
|
|
59
|
+
...
|
|
60
|
+
render: Python
|
|
61
|
+
|
|
62
|
+
- type: textarea
|
|
63
|
+
id: version
|
|
64
|
+
attributes:
|
|
65
|
+
label: Python, fennflow & packages that used by fennflow version
|
|
66
|
+
description: |
|
|
67
|
+
Which version of Python, fennflow, and packages that used by fennflow are you using?
|
|
68
|
+
value: |
|
|
69
|
+
- Python:
|
|
70
|
+
- fennflow:
|
|
71
|
+
- aiobotocore:
|
|
72
|
+
- pydantic:
|
|
73
|
+
validations:
|
|
74
|
+
required: true
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
name: ⚙️ fennflow Engineering Task
|
|
2
|
+
description: "Internal engineering task (CI, tooling, testing, typing, infrastructure)"
|
|
3
|
+
labels: [ "infra" ]
|
|
4
|
+
assignees: [ "Alex-FIR-IT" ]
|
|
5
|
+
|
|
6
|
+
body:
|
|
7
|
+
- type: markdown
|
|
8
|
+
attributes:
|
|
9
|
+
value: Thank you for improving fennflow’s engineering quality ✊
|
|
10
|
+
|
|
11
|
+
- type: textarea
|
|
12
|
+
id: description
|
|
13
|
+
attributes:
|
|
14
|
+
label: Description
|
|
15
|
+
description: |
|
|
16
|
+
Please describe the engineering or infrastructure task in detail.
|
|
17
|
+
|
|
18
|
+
This may include:
|
|
19
|
+
* CI/CD improvements (GitHub Actions, pipelines)
|
|
20
|
+
* Type checking (mypy, pyright)
|
|
21
|
+
* Linting / formatting (ruff, black)
|
|
22
|
+
* Testing infrastructure or coverage improvements
|
|
23
|
+
* Build / packaging / dependency management changes
|
|
24
|
+
|
|
25
|
+
Include context and motivation for why this change is needed.
|
|
26
|
+
validations:
|
|
27
|
+
required: true
|
|
28
|
+
|
|
29
|
+
- type: textarea
|
|
30
|
+
id: impact
|
|
31
|
+
attributes:
|
|
32
|
+
label: Impact
|
|
33
|
+
description: |
|
|
34
|
+
Describe the impact of this change on the development process or codebase quality.
|
|
35
|
+
|
|
36
|
+
Examples:
|
|
37
|
+
* Improved maintainability
|
|
38
|
+
* Faster CI feedback
|
|
39
|
+
* Better type safety
|
|
40
|
+
* Reduced technical debt
|
|
41
|
+
validations:
|
|
42
|
+
required: true
|
|
43
|
+
|
|
44
|
+
- type: textarea
|
|
45
|
+
id: references
|
|
46
|
+
attributes:
|
|
47
|
+
label: References
|
|
48
|
+
description: |
|
|
49
|
+
Add any links, tools, or examples that might help understand the task better.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
name: 🚀 fennflow Feature Request
|
|
2
|
+
description: "Suggest a new feature for fennflow"
|
|
3
|
+
labels: ["enhancement"]
|
|
4
|
+
assignees: ["Alex-FIR-IT"]
|
|
5
|
+
|
|
6
|
+
body:
|
|
7
|
+
- type: markdown
|
|
8
|
+
attributes:
|
|
9
|
+
value: Thank you for contributing to fennflow! ✊
|
|
10
|
+
|
|
11
|
+
- type: textarea
|
|
12
|
+
id: description
|
|
13
|
+
attributes:
|
|
14
|
+
label: Description
|
|
15
|
+
description: |
|
|
16
|
+
Please give as much detail as possible about the feature you would like to suggest. 🙏
|
|
17
|
+
|
|
18
|
+
You might like to add:
|
|
19
|
+
* A demo of how code might look when using the feature
|
|
20
|
+
* Your use case(s) for the feature
|
|
21
|
+
* Reference to other projects that have a similar feature
|
|
22
|
+
validations:
|
|
23
|
+
required: true
|
|
24
|
+
|
|
25
|
+
- type: textarea
|
|
26
|
+
id: references
|
|
27
|
+
attributes:
|
|
28
|
+
label: References
|
|
29
|
+
description: |
|
|
30
|
+
Please add any links or references that might help us understand your feature request better. 📚
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
name: ❓ fennflow Question
|
|
2
|
+
description: "Ask a question about fennflow"
|
|
3
|
+
labels: ["question"]
|
|
4
|
+
assignees: ["Alex-FIR-IT"]
|
|
5
|
+
|
|
6
|
+
body:
|
|
7
|
+
- type: markdown
|
|
8
|
+
attributes:
|
|
9
|
+
value: |
|
|
10
|
+
Thank you for reaching out to the fennflow community! We're here to help! 🤝
|
|
11
|
+
|
|
12
|
+
- type: textarea
|
|
13
|
+
id: question
|
|
14
|
+
attributes:
|
|
15
|
+
label: Question
|
|
16
|
+
description: |
|
|
17
|
+
Please provide as much detail as possible about your question. 🙏
|
|
18
|
+
|
|
19
|
+
You might like to include:
|
|
20
|
+
* Code snippets showing what you've tried
|
|
21
|
+
* Error messages you're encountering (if any)
|
|
22
|
+
* Expected vs actual behavior
|
|
23
|
+
* Your use case and what you're trying to achieve
|
|
24
|
+
validations:
|
|
25
|
+
required: true
|
|
26
|
+
|
|
27
|
+
- type: textarea
|
|
28
|
+
id: context
|
|
29
|
+
attributes:
|
|
30
|
+
label: Additional Context
|
|
31
|
+
description: |
|
|
32
|
+
Please provide any additional context that might help us better understand your question, such as:
|
|
33
|
+
* Your fennflow version
|
|
34
|
+
* Your Python version
|
|
35
|
+
* Relevant configuration or environment details 📝
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
changelog:
|
|
2
|
+
exclude:
|
|
3
|
+
labels:
|
|
4
|
+
- docs
|
|
5
|
+
- chore
|
|
6
|
+
categories:
|
|
7
|
+
- title: 🚀 Features
|
|
8
|
+
labels:
|
|
9
|
+
- enhancement
|
|
10
|
+
- title: 🐛 Bug Fixes
|
|
11
|
+
labels:
|
|
12
|
+
- bug
|
|
13
|
+
- title: 📦 Dependencies
|
|
14
|
+
labels:
|
|
15
|
+
- dependency
|
|
16
|
+
- title: 🔧 Internal
|
|
17
|
+
labels:
|
|
18
|
+
- ci
|
|
19
|
+
- infra
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- master
|
|
7
|
+
pull_request:
|
|
8
|
+
workflow_dispatch:
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
test:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
strategy:
|
|
14
|
+
fail-fast: false
|
|
15
|
+
matrix:
|
|
16
|
+
python-version: [ "3.10", "3.11", "3.12", "3.13", "3.14" ]
|
|
17
|
+
|
|
18
|
+
steps:
|
|
19
|
+
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6
|
|
20
|
+
|
|
21
|
+
- name: Install uv
|
|
22
|
+
uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8
|
|
23
|
+
with:
|
|
24
|
+
python-version: ${{ matrix.python-version }}
|
|
25
|
+
enable-cache: true
|
|
26
|
+
|
|
27
|
+
- name: Install dependencies
|
|
28
|
+
run: uv sync --all-groups
|
|
29
|
+
|
|
30
|
+
- name: Run ruff
|
|
31
|
+
run: uv run ruff check src/
|
|
32
|
+
|
|
33
|
+
- name: Run tests with coverage
|
|
34
|
+
run: uv run pytest --cov=src/fennflow
|
|
35
|
+
env:
|
|
36
|
+
COVERAGE_FILE: .coverage.${{ matrix.python-version }}
|
|
37
|
+
|
|
38
|
+
- name: Upload coverage artifact
|
|
39
|
+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
|
40
|
+
with:
|
|
41
|
+
name: coverage-${{ matrix.python-version }}
|
|
42
|
+
path: .coverage.${{ matrix.python-version }}
|
|
43
|
+
include-hidden-files: true
|
|
44
|
+
|
|
45
|
+
coverage:
|
|
46
|
+
runs-on: ubuntu-latest
|
|
47
|
+
needs: [ test ]
|
|
48
|
+
steps:
|
|
49
|
+
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6
|
|
50
|
+
|
|
51
|
+
- name: Install uv
|
|
52
|
+
uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8
|
|
53
|
+
with:
|
|
54
|
+
enable-cache: true
|
|
55
|
+
|
|
56
|
+
- name: Install dependencies
|
|
57
|
+
run: uv sync --all-groups
|
|
58
|
+
|
|
59
|
+
- name: Download all coverage artifacts
|
|
60
|
+
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8
|
|
61
|
+
with:
|
|
62
|
+
merge-multiple: true
|
|
63
|
+
path: .coverage-data
|
|
64
|
+
|
|
65
|
+
- name: Combine coverage
|
|
66
|
+
run: uv run coverage combine .coverage-data/
|
|
67
|
+
|
|
68
|
+
- name: Convert to XML
|
|
69
|
+
run: uv run coverage xml
|
|
70
|
+
|
|
71
|
+
- name: Upload to Codacy
|
|
72
|
+
uses: codacy/codacy-coverage-reporter-action@89d6c85cfafaec52c72b6c5e8b2878d33104c699 # v1
|
|
73
|
+
with:
|
|
74
|
+
project-token: ${{ secrets.CODACY_PROJECT_TOKEN }}
|
|
75
|
+
coverage-reports: coverage.xml
|
|
76
|
+
|
|
77
|
+
all-tests-passed:
|
|
78
|
+
runs-on: ubuntu-latest
|
|
79
|
+
needs: [ coverage ]
|
|
80
|
+
steps:
|
|
81
|
+
- run: echo "All matrix jobs passed"
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*.*.*"
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
build-and-publish:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
environment: pypi
|
|
12
|
+
permissions:
|
|
13
|
+
id-token: write # required for Trusted Publishing
|
|
14
|
+
contents: write # required to create GitHub Release
|
|
15
|
+
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6
|
|
18
|
+
with:
|
|
19
|
+
fetch-depth: 0 # needed for uv-dynamic-versioning to read git tags
|
|
20
|
+
|
|
21
|
+
- name: Install uv
|
|
22
|
+
uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8
|
|
23
|
+
|
|
24
|
+
- name: Build
|
|
25
|
+
run: uvx hatch build
|
|
26
|
+
|
|
27
|
+
- name: Publish to PyPI
|
|
28
|
+
uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1
|
|
29
|
+
|
|
30
|
+
- name: Create GitHub Release
|
|
31
|
+
uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v3
|
|
32
|
+
with:
|
|
33
|
+
generate_release_notes: true # auto-generates from merged PRs/commits
|
|
34
|
+
files: dist/*
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.13
|
fennflow-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Alex_FIR_IT
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
fennflow-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fennflow
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Atomic-like S3 Framework with Saga Pattern, the Pydantic way
|
|
5
|
+
Project-URL: Homepage, https://github.com/Alex-FIR-IT/FennFlow
|
|
6
|
+
Project-URL: Source, https://github.com/Alex-FIR-IT/FennFlow
|
|
7
|
+
Project-URL: Documentation, https://github.com/Alex-FIR-IT/FennFlow/blob/master/docs/README.md
|
|
8
|
+
Author-email: AlexFIR <osintfir@gmail.com>
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Intended Audience :: Information Technology
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
23
|
+
Classifier: Topic :: Internet
|
|
24
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
25
|
+
Requires-Python: >=3.10
|
|
26
|
+
Requires-Dist: aiobotocore>=3.0.0
|
|
27
|
+
Requires-Dist: certifi>=2023.11.17
|
|
28
|
+
Requires-Dist: pydantic>=2.0.0
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
|
|
31
|
+
<div align="center">
|
|
32
|
+
<a href="https://github.com/Alex-FIR-IT/FennFlow">
|
|
33
|
+
<picture>
|
|
34
|
+
<img src="https://c438939f-e2e4-4a9c-a938-fc6e872413f4.selstorage.ru/github.png" alt="FennFlow">
|
|
35
|
+
</picture>
|
|
36
|
+
</a>
|
|
37
|
+
</div>
|
|
38
|
+
<div align="center">
|
|
39
|
+
<h3>Atomic-like S3 Framework, the Pydantic way</h3>
|
|
40
|
+
</div>
|
|
41
|
+
<div align="center">
|
|
42
|
+
<a href="https://github.com/Alex-FIR-IT/FennFlow/actions/workflows/coverage-report.yml"><img src="https://github.com/Alex-FIR-IT/FennFlow/actions/workflows/coverage-report.yml/badge.svg?branch=master" alt="CI"></a>
|
|
43
|
+
<a href="https://app.codacy.com/gh/Alex-FIR-IT/FennFlow/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_coverage"><img src="https://app.codacy.com/project/badge/Coverage/3db23fc6c6f248f2926731b0bdf0d012" alt="Codacy Coverage"></a>
|
|
44
|
+
<a href="https://github.com/users/Alex-FIR-IT/projects/2/views/2"><img src="https://img.shields.io/badge/Roadmap-green?logo=github" alt="Roadmap"></a>
|
|
45
|
+
<a href="https://github.com/Alex-FIR-IT"><img src="https://img.shields.io/pypi/pyversions/fennflow?logo=python&logoColor=white" alt="versions"></a>
|
|
46
|
+
<a href="https://github.com/Alex-FIR-IT/fennflow/blob/master/LICENSE"><img src="https://img.shields.io/badge/license-MIT-brightgreen" alt="License: MIT"></a>
|
|
47
|
+
<a href="https://github.com/Alex-FIR-IT/fennflow/commits/master/"><img src="https://img.shields.io/github/last-commit/Alex-FIR-IT/fennflow?logo=github" alt="Last Commit"></a>
|
|
48
|
+
<a href="https://app.codacy.com/gh/Alex-FIR-IT/FennFlow/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade"><img src="https://app.codacy.com/project/badge/Grade/3db23fc6c6f248f2926731b0bdf0d012" alt="Codacy Grade"></a>
|
|
49
|
+
</div>
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
**Documentation**: [docs/README.md](https://github.com/Alex-FIR-IT/FennFlow/blob/master/docs/README.md)
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
### <em>FennFlow is a Python s3 framework designed to help you quickly, confidently, and painlessly manipulate files in your object storage implementing Saga-like compensation flow.</em>
|
|
58
|
+
|
|
59
|
+
## Why use FennFlow?
|
|
60
|
+
|
|
61
|
+
Working with aiobotocore often feels like handling raw bytes and dicts. `FennFlow` wraps S3 operations into a high-level
|
|
62
|
+
Unit of Work pattern, providing:
|
|
63
|
+
|
|
64
|
+
- **Atomic-like multistep operations** — if something fails, previous actions are automatically compensated (Saga
|
|
65
|
+
Pattern).
|
|
66
|
+
- **Clean Architecture** — treat S3 as proper repositories using mixins (`PutRepository`, `GetRepository`, etc.).
|
|
67
|
+
- **Pydantic-powered models** — work with `TextContent`, `JsonContent`, `ImageContent` and others instead of raw bytes.
|
|
68
|
+
|
|
69
|
+
## Supported Connectors
|
|
70
|
+
|
|
71
|
+
| Connector | Description | Documentation |
|
|
72
|
+
|---------------------|----------------------------------------------|---------------------------------------------|
|
|
73
|
+
| AWS S3 | s3 compatible object storage via aiobotocore | [Quick start](docs/connectors/s3.md) |
|
|
74
|
+
| In-Memory (default) | great for and tests and development | [Quick start](docs/connectors/in-memory.md) |
|
|
75
|
+
|
|
76
|
+
## Supported Backends
|
|
77
|
+
|
|
78
|
+
FennFlow uses backend as a source of truth for your file storage.
|
|
79
|
+
No matter what your file storage contains, backend ensures your data is consistent.
|
|
80
|
+
|
|
81
|
+
| Backend | Description | Documenration |
|
|
82
|
+
|---------------------|-----------------------------------------------------|-------------------------------------------|
|
|
83
|
+
| In-Memory (default) | great for and tests, development and small projects | [Quick start](docs/backends/in-memory.md) |
|
|
84
|
+
|
|
85
|
+
### Backend Comparison
|
|
86
|
+
|
|
87
|
+
| | Raw aiobotocore | In-Memory |
|
|
88
|
+
|--------------------|---------------------------------------------------|-----------------------------------------------------------------------------------------------|
|
|
89
|
+
| **Consistency** | 🔴 None<br>No link between files and metadata | 🟡 Medium<br>Consistent within process lifetime, lost on crash |
|
|
90
|
+
| **Compensation** | 🔴 None<br>Orphaned files on failure | 🟡 Medium<br>Automatic within process, orphaned files possible on crash |
|
|
91
|
+
| **Reliability** | 🔴 Low<br>Failures leave storage in unknown state | 🟡 Medium<br>Syncs with storage on restart, files uploaded during crash cannot be compensated |
|
|
92
|
+
| **Latency** | ✅ Lowest<br>Pure S3 network overhead only | ✅ Lowest<br>Minimal in-process overhead |
|
|
93
|
+
| **Infrastructure** | ✅ None | ✅ None |
|
|
94
|
+
| **Memory usage** | ✅ None | 🟡 Stores file metadata in-process |
|
|
95
|
+
|
|
96
|
+
## Quick Start
|
|
97
|
+
|
|
98
|
+
Here's a minimal example of FennFlow:
|
|
99
|
+
|
|
100
|
+
```python3
|
|
101
|
+
import asyncio
|
|
102
|
+
|
|
103
|
+
from fennflow import ConfigDict, UnitOfWork
|
|
104
|
+
from fennflow.backends import InMemoryBackendConfig
|
|
105
|
+
from fennflow.connectors import S3ConnectorConfig
|
|
106
|
+
from fennflow.files import BinaryContent, JsonContent, TextContent
|
|
107
|
+
from fennflow.repositories import (
|
|
108
|
+
DeleteRepository,
|
|
109
|
+
GetRepository,
|
|
110
|
+
ListRepository,
|
|
111
|
+
PutRepository,
|
|
112
|
+
S3RepoField,
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
# 1. Define your repository with mixins
|
|
117
|
+
class CrudRepository(
|
|
118
|
+
PutRepository,
|
|
119
|
+
DeleteRepository,
|
|
120
|
+
GetRepository,
|
|
121
|
+
ListRepository,
|
|
122
|
+
):
|
|
123
|
+
pass
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
# 2. Set up your Unit of Work
|
|
127
|
+
class UOW(UnitOfWork):
|
|
128
|
+
my_files = S3RepoField(CrudRepository, bucket_name="my_files")
|
|
129
|
+
config = ConfigDict(
|
|
130
|
+
backend=InMemoryBackendConfig(),
|
|
131
|
+
connector=S3ConnectorConfig(),
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
async def main():
|
|
136
|
+
text_file = TextContent.from_content("Hello, world!")
|
|
137
|
+
json_file = JsonContent.from_content([1, 2, 3])
|
|
138
|
+
binary_file = BinaryContent(data=b"some bytes", media_type="text/plain")
|
|
139
|
+
|
|
140
|
+
async with UOW() as uow:
|
|
141
|
+
await uow.my_files.at("folder1").put(
|
|
142
|
+
text_file,
|
|
143
|
+
json_file,
|
|
144
|
+
binary_file,
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
paths = await uow.my_files.at("folder1").list()
|
|
148
|
+
print(paths) # ListResponse[Filepath, ...]
|
|
149
|
+
|
|
150
|
+
files = await uow.my_files.get(*paths)
|
|
151
|
+
print(files) # MediaResponse[TextContent, JsonContent, BinaryContent]
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
if __name__ == "__main__":
|
|
155
|
+
asyncio.run(main())
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## Next Steps
|
|
159
|
+
|
|
160
|
+
Read the [docs](https://github.com/Alex-FIR-IT/FennFlow/blob/master/docs/README.md) to learn more about working with
|
|
161
|
+
FennFlow.
|
|
162
|
+
|