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.
Files changed (126) hide show
  1. fennflow-0.1.0/.github/ISSUE_TEMPLATE/bug-notice.yaml +74 -0
  2. fennflow-0.1.0/.github/ISSUE_TEMPLATE/engineering-task.yaml +49 -0
  3. fennflow-0.1.0/.github/ISSUE_TEMPLATE/feature-request.yaml +30 -0
  4. fennflow-0.1.0/.github/ISSUE_TEMPLATE/question.yaml +35 -0
  5. fennflow-0.1.0/.github/dependabot.yml +12 -0
  6. fennflow-0.1.0/.github/pull_request_template.md +6 -0
  7. fennflow-0.1.0/.github/release.yml +19 -0
  8. fennflow-0.1.0/.github/workflows/coverage-report.yml +81 -0
  9. fennflow-0.1.0/.github/workflows/publish.yml +34 -0
  10. fennflow-0.1.0/.gitignore +16 -0
  11. fennflow-0.1.0/.python-version +1 -0
  12. fennflow-0.1.0/LICENSE +21 -0
  13. fennflow-0.1.0/PKG-INFO +162 -0
  14. fennflow-0.1.0/README.md +132 -0
  15. fennflow-0.1.0/mkdocs.yml +16 -0
  16. fennflow-0.1.0/pyproject.toml +144 -0
  17. fennflow-0.1.0/src/fennflow/__init__.py +7 -0
  18. fennflow-0.1.0/src/fennflow/_base_exception.py +2 -0
  19. fennflow-0.1.0/src/fennflow/_datetime.py +10 -0
  20. fennflow-0.1.0/src/fennflow/_decorators.py +33 -0
  21. fennflow-0.1.0/src/fennflow/_new_types.py +7 -0
  22. fennflow-0.1.0/src/fennflow/_operations/__init__.py +0 -0
  23. fennflow-0.1.0/src/fennflow/_operations/context/__init__.py +0 -0
  24. fennflow-0.1.0/src/fennflow/_operations/context/abstract.py +6 -0
  25. fennflow-0.1.0/src/fennflow/_operations/context/delete.py +15 -0
  26. fennflow-0.1.0/src/fennflow/_operations/context/put.py +14 -0
  27. fennflow-0.1.0/src/fennflow/_operations/context/types.py +7 -0
  28. fennflow-0.1.0/src/fennflow/_operations/dto.py +79 -0
  29. fennflow-0.1.0/src/fennflow/_operations/enums.py +19 -0
  30. fennflow-0.1.0/src/fennflow/_operations/executor.py +47 -0
  31. fennflow-0.1.0/src/fennflow/_operations/flows/__init__.py +0 -0
  32. fennflow-0.1.0/src/fennflow/_operations/flows/abstract.py +33 -0
  33. fennflow-0.1.0/src/fennflow/_operations/flows/delete.py +69 -0
  34. fennflow-0.1.0/src/fennflow/_operations/flows/put.py +52 -0
  35. fennflow-0.1.0/src/fennflow/_operations/registry.py +15 -0
  36. fennflow-0.1.0/src/fennflow/_path.py +31 -0
  37. fennflow-0.1.0/src/fennflow/_reconciler/__init__.py +10 -0
  38. fennflow-0.1.0/src/fennflow/_reconciler/_orchestrator.py +86 -0
  39. fennflow-0.1.0/src/fennflow/_reconciler/config.py +10 -0
  40. fennflow-0.1.0/src/fennflow/_reconciler/core.py +112 -0
  41. fennflow-0.1.0/src/fennflow/_reconciler/enums.py +29 -0
  42. fennflow-0.1.0/src/fennflow/_reconciler/exceptions.py +6 -0
  43. fennflow-0.1.0/src/fennflow/_resolver.py +62 -0
  44. fennflow-0.1.0/src/fennflow/_sentinel.py +69 -0
  45. fennflow-0.1.0/src/fennflow/backends/__init__.py +7 -0
  46. fennflow-0.1.0/src/fennflow/backends/abstract/__init__.py +0 -0
  47. fennflow-0.1.0/src/fennflow/backends/abstract/annotations.py +30 -0
  48. fennflow-0.1.0/src/fennflow/backends/abstract/base.py +95 -0
  49. fennflow-0.1.0/src/fennflow/backends/abstract/config.py +15 -0
  50. fennflow-0.1.0/src/fennflow/backends/enums.py +9 -0
  51. fennflow-0.1.0/src/fennflow/backends/exceptions.py +9 -0
  52. fennflow-0.1.0/src/fennflow/backends/factory.py +22 -0
  53. fennflow-0.1.0/src/fennflow/backends/in_memory/__init__.py +7 -0
  54. fennflow-0.1.0/src/fennflow/backends/in_memory/_select.py +124 -0
  55. fennflow-0.1.0/src/fennflow/backends/in_memory/base.py +214 -0
  56. fennflow-0.1.0/src/fennflow/backends/in_memory/config.py +9 -0
  57. fennflow-0.1.0/src/fennflow/backends/registry.py +5 -0
  58. fennflow-0.1.0/src/fennflow/backends/responses.py +33 -0
  59. fennflow-0.1.0/src/fennflow/backends/types/__init__.py +0 -0
  60. fennflow-0.1.0/src/fennflow/backends/types/config.py +5 -0
  61. fennflow-0.1.0/src/fennflow/connectors/__init__.py +11 -0
  62. fennflow-0.1.0/src/fennflow/connectors/abstract/__init__.py +3 -0
  63. fennflow-0.1.0/src/fennflow/connectors/abstract/base.py +88 -0
  64. fennflow-0.1.0/src/fennflow/connectors/factory.py +40 -0
  65. fennflow-0.1.0/src/fennflow/connectors/in_memory/__init__.py +7 -0
  66. fennflow-0.1.0/src/fennflow/connectors/in_memory/config.py +9 -0
  67. fennflow-0.1.0/src/fennflow/connectors/in_memory/connector.py +152 -0
  68. fennflow-0.1.0/src/fennflow/connectors/registry.py +9 -0
  69. fennflow-0.1.0/src/fennflow/connectors/s3/__init__.py +7 -0
  70. fennflow-0.1.0/src/fennflow/connectors/s3/client.py +76 -0
  71. fennflow-0.1.0/src/fennflow/connectors/s3/config.py +43 -0
  72. fennflow-0.1.0/src/fennflow/connectors/s3/connector.py +182 -0
  73. fennflow-0.1.0/src/fennflow/connectors/types/__init__.py +0 -0
  74. fennflow-0.1.0/src/fennflow/connectors/types/config.py +6 -0
  75. fennflow-0.1.0/src/fennflow/core/__init__.py +5 -0
  76. fennflow-0.1.0/src/fennflow/core/configs/__init__.py +0 -0
  77. fennflow-0.1.0/src/fennflow/core/configs/base.py +28 -0
  78. fennflow-0.1.0/src/fennflow/exceptions/__init__.py +28 -0
  79. fennflow-0.1.0/src/fennflow/files/__init__.py +23 -0
  80. fennflow-0.1.0/src/fennflow/files/exceptions/__init__.py +18 -0
  81. fennflow-0.1.0/src/fennflow/files/exceptions/base.py +5 -0
  82. fennflow-0.1.0/src/fennflow/files/exceptions/cannot_parse_extension.py +13 -0
  83. fennflow-0.1.0/src/fennflow/files/exceptions/extension_cannot_be_guessed.py +5 -0
  84. fennflow-0.1.0/src/fennflow/files/exceptions/filename_and_mediatype_both_none.py +12 -0
  85. fennflow-0.1.0/src/fennflow/files/exceptions/filename_is_none.py +8 -0
  86. fennflow-0.1.0/src/fennflow/files/exceptions/media_type_cannot_be_guessed.py +23 -0
  87. fennflow-0.1.0/src/fennflow/files/exceptions/storage_prefix_is_none.py +8 -0
  88. fennflow-0.1.0/src/fennflow/files/factory.py +115 -0
  89. fennflow-0.1.0/src/fennflow/files/media/__init__.py +21 -0
  90. fennflow-0.1.0/src/fennflow/files/media/abstract/__init__.py +0 -0
  91. fennflow-0.1.0/src/fennflow/files/media/abstract/content.py +21 -0
  92. fennflow-0.1.0/src/fennflow/files/media/abstract/from_content.py +41 -0
  93. fennflow-0.1.0/src/fennflow/files/media/audio_content.py +11 -0
  94. fennflow-0.1.0/src/fennflow/files/media/base.py +163 -0
  95. fennflow-0.1.0/src/fennflow/files/media/binary_content.py +32 -0
  96. fennflow-0.1.0/src/fennflow/files/media/image_content.py +13 -0
  97. fennflow-0.1.0/src/fennflow/files/media/json_content.py +71 -0
  98. fennflow-0.1.0/src/fennflow/files/media/pdf_content.py +5 -0
  99. fennflow-0.1.0/src/fennflow/files/media/text_content.py +62 -0
  100. fennflow-0.1.0/src/fennflow/files/media/url_content.py +11 -0
  101. fennflow-0.1.0/src/fennflow/files/media/video_content.py +15 -0
  102. fennflow-0.1.0/src/fennflow/files/protocols/__init__.py +0 -0
  103. fennflow-0.1.0/src/fennflow/files/protocols/has_data.py +15 -0
  104. fennflow-0.1.0/src/fennflow/files/protocols/has_data_and_ext.py +20 -0
  105. fennflow-0.1.0/src/fennflow/files/protocols/has_data_and_ext_obj.py +19 -0
  106. fennflow-0.1.0/src/fennflow/files/protocols/has_ext.py +20 -0
  107. fennflow-0.1.0/src/fennflow/files/registry.py +21 -0
  108. fennflow-0.1.0/src/fennflow/files/responses/__init__.py +0 -0
  109. fennflow-0.1.0/src/fennflow/files/responses/base.py +127 -0
  110. fennflow-0.1.0/src/fennflow/files/responses/list.py +45 -0
  111. fennflow-0.1.0/src/fennflow/files/types.py +26 -0
  112. fennflow-0.1.0/src/fennflow/py.typed +0 -0
  113. fennflow-0.1.0/src/fennflow/repositories/__init__.py +14 -0
  114. fennflow-0.1.0/src/fennflow/repositories/at.py +60 -0
  115. fennflow-0.1.0/src/fennflow/repositories/base.py +30 -0
  116. fennflow-0.1.0/src/fennflow/repositories/delete.py +59 -0
  117. fennflow-0.1.0/src/fennflow/repositories/fields/__init__.py +7 -0
  118. fennflow-0.1.0/src/fennflow/repositories/fields/base.py +81 -0
  119. fennflow-0.1.0/src/fennflow/repositories/fields/s3.py +44 -0
  120. fennflow-0.1.0/src/fennflow/repositories/get.py +73 -0
  121. fennflow-0.1.0/src/fennflow/repositories/list.py +57 -0
  122. fennflow-0.1.0/src/fennflow/repositories/put.py +70 -0
  123. fennflow-0.1.0/src/fennflow/uow/__init__.py +5 -0
  124. fennflow-0.1.0/src/fennflow/uow/core.py +209 -0
  125. fennflow-0.1.0/src/fennflow/uow/inspector.py +23 -0
  126. 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,12 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: "github-actions"
4
+ directory: "/"
5
+ schedule:
6
+ interval: monthly
7
+ cooldown:
8
+ default-days: 7
9
+ groups:
10
+ github-actions:
11
+ patterns:
12
+ - "*"
@@ -0,0 +1,6 @@
1
+ - Closes #<issue>
2
+
3
+ ### Checklist
4
+
5
+ - [ ] Any **AI generated code** has been reviewed line-by-line by the human PR author, who stands by it.
6
+ - [ ] **PR title** is fit for the [release changelog](https://github.com/Alex-FIR-IT/FennFlow/releases).
@@ -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,16 @@
1
+ # Python-generated files
2
+ __pycache__/
3
+ *.py[oc]
4
+ build/
5
+ dist/
6
+ wheels/
7
+ *.egg-info
8
+
9
+ # Virtual environments
10
+ .venv
11
+
12
+ .idea
13
+ .env
14
+ .notes
15
+ site
16
+ main.py
@@ -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.
@@ -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
+