{"id":30811647,"url":"https://github.com/valian/python-business-logic","last_synced_at":"2026-03-08T20:32:29.340Z","repository":{"id":57456759,"uuid":"97376587","full_name":"Valian/python-business-logic","owner":"Valian","description":"Python package that makes creating complicated business logic easy","archived":false,"fork":false,"pushed_at":"2022-12-26T20:29:35.000Z","size":78,"stargazers_count":82,"open_issues_count":3,"forks_count":7,"subscribers_count":9,"default_branch":"master","last_synced_at":"2025-08-27T08:40:59.099Z","etag":null,"topics":["business-logic","business-rules","logic","python"],"latest_commit_sha":null,"homepage":"https://python-business-logic.readthedocs.io","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Valian.png","metadata":{"files":{"readme":"README.rst","changelog":"HISTORY.rst","contributing":"CONTRIBUTING.rst","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-07-16T10:40:47.000Z","updated_at":"2025-07-18T18:48:17.000Z","dependencies_parsed_at":"2023-01-31T01:15:23.935Z","dependency_job_id":null,"html_url":"https://github.com/Valian/python-business-logic","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/Valian/python-business-logic","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Valian%2Fpython-business-logic","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Valian%2Fpython-business-logic/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Valian%2Fpython-business-logic/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Valian%2Fpython-business-logic/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Valian","download_url":"https://codeload.github.com/Valian/python-business-logic/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Valian%2Fpython-business-logic/sbom","scorecard":{"id":147113,"data":{"date":"2025-08-11","repo":{"name":"github.com/Valian/python-business-logic","commit":"49c27b48b321dd5a401b900116c53aec29af2f8e"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":2.9,"checks":[{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Code-Review","score":1,"reason":"Found 2/20 approved changesets -- score normalized to 1","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":0,"reason":"Project has not signed or included provenance with any releases.","details":["Warn: release artifact v0.2.0 not signed: https://api.github.com/repos/Valian/python-business-logic/releases/12924388","Warn: release artifact v0.1.1 not signed: https://api.github.com/repos/Valian/python-business-logic/releases/8233522","Warn: release artifact v0.1.0 not signed: https://api.github.com/repos/Valian/python-business-logic/releases/7661267","Warn: release artifact v0.2.0 does not have provenance: https://api.github.com/repos/Valian/python-business-logic/releases/12924388","Warn: release artifact v0.1.1 does not have provenance: https://api.github.com/repos/Valian/python-business-logic/releases/8233522","Warn: release artifact v0.1.0 does not have provenance: https://api.github.com/repos/Valian/python-business-logic/releases/7661267"],"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Vulnerabilities","score":8,"reason":"2 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: PYSEC-2022-43017 / GHSA-qwmp-2cf2-g9g6","Warn: Project is vulnerable to: PYSEC-2022-238 / GHSA-h3qr-fjhm-jphw"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 14 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-16T09:36:23.846Z","repository_id":57456759,"created_at":"2025-08-16T09:36:23.846Z","updated_at":"2025-08-16T09:36:23.846Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":273868148,"owners_count":25182423,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-06T02:00:13.247Z","response_time":2576,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["business-logic","business-rules","logic","python"],"created_at":"2025-09-06T06:47:40.254Z","updated_at":"2026-03-08T20:32:29.311Z","avatar_url":"https://github.com/Valian.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"=====================\nPython Business Logic\n=====================\n\n.. image:: https://badge.fury.io/py/python-business-logic.svg\n    :target: https://badge.fury.io/py/python-business-logic\n\n.. image:: https://travis-ci.org/Valian/python-business-logic.svg?branch=master\n    :target: https://travis-ci.org/Valian/python-business-logic\n\n.. image:: https://codecov.io/gh/Valian/python-business-logic/branch/master/graph/badge.svg\n    :target: https://codecov.io/gh/Valian/python-business-logic\n\n.. image:: https://readthedocs.org/projects/python-business-logic/badge/?version=latest\n    :target: http://python-business-logic.readthedocs.io\n\n\nTraditionally, most web applications are written using MVC pattern. Python Business Logic helps you to add *Business Layer*, also called *Application Layer*, that is dependent only on models and composed of simple functions. Code written this way is extremely easy to read, test, and use in different scenarios. Package has no dependencies and can be used in any web framework, like Django, Flask, Bottle and others.\n\nDocumentation\n-------------\n\nThe full documentation is at https://python-business-logic.readthedocs.io. Still under development. \n\nInstallation\n------------\n\nInstall Python Business Logic::\n\n    pip install python-business-logic\n\n\nGetting Started\n---------------\n\nCore elements of library are validators, functions created to ensure that business logic is correct:\n\n.. code:: python\n\n    \u003e\u003e\u003e from business_logic.core import validator\n\n    \u003e\u003e\u003e @validator\n    ... def can_watch_movie(user, movie):\n    ...     # some example business logic, it can be as complex as you want\n    ...     return user.is_parent or user.age \u003e= movie.age_restriction\n\n\n\nWith validators you can decorate actions performed that will be checked against that validator:\n\n.. code:: python\n\n    \u003e\u003e\u003e from business_logic.core import validated_by\n\n    \u003e\u003e\u003e @validated_by(can_watch_movie)\n    ... def watch_movie(user, movie):\n    ...     print(\"'{}' is watching movie '{}'\".format(user.name, movie.name))\n\n\n\nAs you can see, arguments to validator must match those passed to function.\nNow every call to :code:`watch_movie` will require that validator :code:`can_watch_movie` passes:\n\n.. code:: python\n\n    \u003e\u003e\u003e import collections\n    \u003e\u003e\u003e User = collections.namedtuple('User', ['name', 'age', 'is_parent'])\n    \u003e\u003e\u003e Movie = collections.namedtuple('Movie', ['name', 'age_restriction'])\n    \u003e\u003e\u003e alice = User('Alice', 32, True)\n    \u003e\u003e\u003e bob = User('Bob', 6, False)\n    \u003e\u003e\u003e cartoon = Movie('Tom\u0026Jerry', 0)\n    \u003e\u003e\u003e horror = Movie('Scream', 18)\n\n    \u003e\u003e\u003e watch_movie(bob, cartoon)\n    'Bob' is watching movie 'Tom\u0026Jerry'\n    \n    \u003e\u003e\u003e watch_movie(alice, horror)\n    'Alice' is watching movie 'Scream'\n    \n    \u003e\u003e\u003e watch_movie(bob, horror) # doctest: +IGNORE_EXCEPTION_DETAIL\n    Traceback (most recent call last):\n    business_logic.exceptions.LogicException: Validation failed!\n\n\nYou can skip validation using :code:`validate=False`:\n\n.. code:: python\n\n    \u003e\u003e\u003e watch_movie(user=bob, movie=horror, validate=False)\n    'Bob' is watching movie 'Scream'\n\n\n\nAlso, if we just want to know if action is permitted, just let's run:\n\n.. code:: python\n\n    \u003e\u003e\u003e validation = can_watch_movie(bob, horror, raise_exception=False)\n    \u003e\u003e\u003e validation\n    \u003cPermissionResult success=False error=Validation failed!\u003e\n    \n    \u003e\u003e\u003e bool(validation)\n    False\n    \n    \u003e\u003e\u003e validation.error  # it's an actual exception\n    LogicException('Validation failed!', error_code=None)\n\n\n\nChaining validators is really easy and readable:\n\n.. code:: python\n\n   \u003e\u003e\u003e @validator\n   ... def is_old_enough(user, movie):\n   ...     return user.age \u003e= movie.age_restriction\n\n   \u003e\u003e\u003e @validator\n   ... def can_watch_movie(user, movie):\n   ...     is_old_enough(user, movie)\n   ...     # we don't have to return anything, @validator makes use of exceptions\n\n   \u003e\u003e\u003e can_watch_movie(bob, horror)  # doctest: +IGNORE_EXCEPTION_DETAIL\n   Traceback (most recent call last):\n   business_logic.exceptions.LogicException: Validation failed!\n\n\n\nOk, but we're still missing something. We don't know why exactly validation failed,\nall we have is a generic \"Validation failed!\" message. How to fix that? It's easy, let's\nmake our own errors!\n\n.. code:: python\n\n   \u003e\u003e\u003e from business_logic import LogicErrors, LogicException\n   \u003e\u003e\u003e class AgeRestrictionErrors(LogicErrors):\n   ...     CANT_WATCH_MOVIE_TOO_YOUNG = LogicException(\"User is too young to watch this\")\n\n   \u003e\u003e\u003e @validator\n   ... def is_old_enough(user, movie):\n   ...     if user.age \u003c movie.age_restriction:\n   ...          raise AgeRestrictionErrors.CANT_WATCH_MOVIE_TOO_YOUNG\n\n   \u003e\u003e\u003e is_old_enough(bob, horror)  # doctest: +IGNORE_EXCEPTION_DETAIL\n   Traceback (most recent call last):\n   business_logic.exceptions.LogicException: User is too young to watch this\n\n   \u003e\u003e\u003e # we can also obtain exception details like this\n   \u003e\u003e\u003e result = is_old_enough(bob, horror, raise_exception=False)\n   \u003e\u003e\u003e bool(result)\n   False\n   \n   \u003e\u003e\u003e result.error\n   LogicException('User is too young to watch this', error_code='CANT_WATCH_MOVIE_TOO_YOUNG')\n   \n   \u003e\u003e\u003e result.error_code == 'CANT_WATCH_MOVIE_TOO_YOUNG'\n   True\n   \n   \u003e\u003e\u003e # result.errors is shortcut to registry with all errors\n   \u003e\u003e\u003e result.error == result.errors['CANT_WATCH_MOVIE_TOO_YOUNG']\n   True\n\n\n\nTesting is really easy:\n\n.. code:: python\n\n   \u003e\u003e\u003e def test_user_cant_watch_movie_if_under_age_restriction():\n   ...    bob = User('Bob', 6, False)\n   ...    horror = Movie('Scream', 18)\n   ...    result = is_old_enough(bob, horror, raise_exception=False)\n   ...    # There are two ways to check if expected exceptions was raised\n   ...    assert result.error_code == 'CANT_WATCH_MOVIE_TOO_YOUNG'\n   ...    assert result.error == AgeRestrictionErrors.CANT_WATCH_MOVIE_TOO_YOUNG\n\n   \u003e\u003e\u003e test_user_cant_watch_movie_if_under_age_restriction()\n\n\nAlso, if you need to display parametrizable error messages, just use `.format` method\n\n.. code:: python\n   \u003e\u003e\u003e\n   \u003e\u003e\u003e exc = LogicException('User {user} is way too young!', error_code='TOO_YOUNG')\n   \u003e\u003e\u003e formatted_exc = exc.format(user='Bob')\n   \u003e\u003e\u003e assert str(formatted_exc) == 'User Bob is way too young!'\n   \u003e\u003e\u003e assert exc.error_code == formatted_exc.error_code\n   \u003e\u003e\u003e assert exc == formatted_exc\n\nUsage\n-----\n\nWhen using this package, you should write all your business logic as simple functions, using only\ninputs and Database Layer (eg. `Django ORM or SQLAlchemy`). This way, you can easily test your\nlogic and use it in any way you like. Convention that I follow is to put all functions inside `logic.py` file or `logic` submodule.\n\nIn **views** and **API** calls: Your role is to prepare all required data for business function (from forms, user session etc), call function\nand present results to user. Middleware catching LogicException and, for example, displaying message to user in a generic way\ncan improve readability a lot, because no exception handling need to be done in view.\n\nAs **management commands**: In Django you can create custom `management command`, that allows you to use cli to perform custom logic.\nPython Business Logic functions works very well for that use case!\n\nFrom **external code**: Just import your function and use it. Since there shouldn't be any framework-related\ninputs other than Database Models, usage is really simple. In reality, your business functions form *business API* of your application.\n\nExamples\n--------\n\nFor examples how to use this library, look into directory *examples*. Currently there is only one called *Football match*. Most important file there is :code:`logic.py`.\n\n\nRunning Tests\n-------------\n\nDoes the code actually work?\n\n::\n\n    $ pip install -r requirements_test.txt\n    $ tox\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvalian%2Fpython-business-logic","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvalian%2Fpython-business-logic","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvalian%2Fpython-business-logic/lists"}