{"id":50171873,"url":"https://github.com/zinthose/pureshell","last_synced_at":"2026-05-24T23:38:19.208Z","repository":{"id":298946182,"uuid":"1001259424","full_name":"zinthose/pureshell","owner":"zinthose","description":"\"A Python module for impartial computation. Implements the Functional Core, Stateful Shell pattern.","archived":false,"fork":false,"pushed_at":"2025-06-21T15:50:58.000Z","size":58,"stargazers_count":0,"open_issues_count":11,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-05-24T23:38:18.133Z","etag":null,"topics":["functional-programming","pygame","python","python3"],"latest_commit_sha":null,"homepage":"","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/zinthose.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-06-13T04:55:23.000Z","updated_at":"2025-06-16T12:48:32.000Z","dependencies_parsed_at":"2025-06-13T19:25:52.720Z","dependency_job_id":"ac8e6fa9-d63d-4679-b6f0-c629de1cbd44","html_url":"https://github.com/zinthose/pureshell","commit_stats":null,"previous_names":["zinthose/pureshell"],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/zinthose/pureshell","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zinthose%2Fpureshell","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zinthose%2Fpureshell/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zinthose%2Fpureshell/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zinthose%2Fpureshell/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/zinthose","download_url":"https://codeload.github.com/zinthose/pureshell/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zinthose%2Fpureshell/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33455025,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-24T19:21:36.376Z","status":"ssl_error","status_checked_at":"2026-05-24T19:21:10.562Z","response_time":57,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["functional-programming","pygame","python","python3"],"created_at":"2026-05-24T23:38:17.025Z","updated_at":"2026-05-24T23:38:19.198Z","avatar_url":"https://github.com/zinthose.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# 🐚 pureshell\n\nA Python design pattern for creating Stateful Entities powered by a pure Functional Core. This library provides a simple, enforceable, and testable architecture for separating state management from business logic.\n\n[![PyPI version](https://badge.fury.io/py/pureshell.svg)](https://badge.fury.io/py/pureshell)\n\n## 🏛️ The \"Stateful Shell\" Pattern\n\nAt its core, `pureshell` helps you implement the \"Functional Core, Stateful Shell\" pattern. This means:\n\n* **🧠 Functional Core**: All your complex business logic (e.g., game rules, business calculations) is written as pure, stateless functions. These functions are grouped into `Ruleset` classes and are incredibly easy to unit test.\n* **🤖 Stateful Shell**: Your objects (`StatefulEntity` classes) act as simple \"shells\" that hold state. They delegate all their logic and calculations to the functional core, keeping the classes themselves clean, declarative, and free of implementation details.\n\nThis separation makes your system more robust, highly testable, and easier to reason about as it grows in complexity.\n\n## ✨ Core Concepts\n\nThe framework is built around a few key components:\n\n* `StatefulEntity`: A base class for your objects (`ShoppingCart`, `GameCharacter`, etc.). It enforces the pattern by ensuring no business logic is accidentally added to the class itself.\n* `Ruleset`: A base class for your collections of pure functions (e.g., `CartRules`, `CharacterRules`). It enforces that all rules are defined as `@staticmethod`.\n* `@ruleset_provider(RulesetClass)`: A class decorator that links a `StatefulEntity` to its corresponding `Ruleset`.\n* `@shell_method(...)`: A method decorator that declaratively links a method on a `StatefulEntity` to a pure function in its `Ruleset`.\n* `@side_effect_method`: A decorator to explicitly mark methods that perform I/O (like printing to the console or rendering graphics) as being exempt from the \"pure logic\" enforcement.\n\n### New in version 0.2.0: Dynamic Ruleset Injection\n\nYou can now inject a `Ruleset` instance directly when creating a `StatefulEntity`. This allows for more flexible and dynamic behavior, especially useful for scenarios like:\n\n* **Strategy Pattern:** Easily switch between different sets of rules at runtime.\n* **Testing:** Inject mock or simplified rulesets for testing specific behaviors.\n* **Configuration-driven Behavior:** Load different rulesets based on configuration files or user settings.\n\nIf a `ruleset_instance` is provided during `StatefulEntity` instantiation, it will be used instead of the one specified by the `@ruleset_provider` decorator.\n\n```python\n# Example of dynamic ruleset injection\nfrom pureshell import StatefulEntity, Ruleset, shell_method\n\nclass BehaviorA(Ruleset):\n    @staticmethod\n    def act(state_data: dict) -\u003e dict:\n        print(\"Performing Action A\")\n        return {**state_data, \"action_taken\": \"A\"}\n\nclass BehaviorB(Ruleset):\n    @staticmethod\n    def act(state_data: dict) -\u003e dict:\n        print(\"Performing Action B\")\n        return {**state_data, \"action_taken\": \"B\"}\n\nclass MyEntity(StatefulEntity):\n    def __init__(self, initial_state: dict, ruleset_instance: Ruleset = None):\n        # StatefulEntity.__init__ now handles initial_state and ruleset_instance\n        super().__init__(initial_state, ruleset_instance)\n\n    @shell_method(\"state_data\", pure_func=\"act\", mutates=True)\n    def perform_action(self) -\u003e None:\n        pass # Logic delegated to ruleset's 'act' method\n\n# Create entity with default behavior (if a @ruleset_provider is set)\n# entity_default = MyEntity(initial_state={})\n\n# Create entity with BehaviorA\nentity_a = MyEntity(initial_state={\"state_data\": {}}, ruleset_instance=BehaviorA())\nentity_a.perform_action() # Will use BehaviorA.act\n\n# Create entity with BehaviorB\nentity_b = MyEntity(initial_state={\"state_data\": {}}, ruleset_instance=BehaviorB())\nentity_b.perform_action() # Will use BehaviorB.act\n```\n\n## 🚀 Installation\n\nTo use `pureshell` in your project, it's highly recommended to work within a virtual environment.\n\n1. **Create and activate a virtual environment:**\n\n   ```bash\n   # Windows\n   python -m venv .venv\n   .venv\\\\Scripts\\\\activate\n\n   # macOS/Linux\n   python3 -m venv .venv\n   source .venv/bin/activate\n   ```\n\n2. **Install from PyPI (for users of the library):**\n\n   ```bash\n   pip install pureshell\n   ```\n\n   Alternatively, if you have a `requirements.txt` file that includes `pureshell`:\n\n   ```bash\n   pip install -r requirements.txt\n   ```\n\n3. **For development (if you've cloned this repository):**\n\n   Install the project and its development dependencies:\n\n   ```bash\n   pip install -e .\n   pip install -r requirements-dev.txt\n   ```\n\n   This will install the project in editable mode and all tools needed for testing, linting, formatting, etc.\n\n## 🚀 Usage\n\nHere's a practical example of defining a `ShoppingCart` using the `pureshell` pattern. This example is available in `examples/shopping_cart_example.py`.\n\n**1. Define your data structures and pure functions:**\n\nFirst, we define our data classes and a `Ruleset` containing all the business logic as pure, static methods.\n\n```python\n# In examples/shopping_cart_example.py\nfrom dataclasses import dataclass\nfrom typing import List\nfrom pureshell import Ruleset\n\n@dataclass(frozen=True)\nclass CartItem:\n    \"\"\"Represents an item in the shopping cart.\"\"\"\n    name: str\n    price: float\n    requires_age_check: bool = False\n\n@dataclass(frozen=True)\nclass UserProfile:\n    \"\"\"Represents basic user profile data.\"\"\"\n    user_id: str\n    age: int\n\nclass CartRules(Ruleset):\n    \"\"\"A collection of pure functions for cart logic.\"\"\"\n    @staticmethod\n    def add_item(items: List[CartItem], new_item: CartItem) -\u003e List[CartItem]:\n        \"\"\"Pure function to add an item to a list of cart items.\"\"\"\n        return items + [new_item]\n\n    @staticmethod\n    def calculate_total(items: List[CartItem]) -\u003e float:\n        \"\"\"Pure function to calculate the total price of all items in a list.\"\"\"\n        return sum(item.price for item in items)\n\n    @staticmethod\n    def is_valid(items: List[CartItem], profile: UserProfile) -\u003e bool:\n        \"\"\"Checks if a cart is valid based on items and user profile.\"\"\"\n        if any(item.requires_age_check for item in items):\n            return profile.age \u003e= 21\n        return True\n```\n\n**2. Define your stateful entity:**\n\nNext, create the `ShoppingCart` class. It inherits from `StatefulEntity`, holds the state, and uses `@shell_method` to declaratively link its methods to the pure functions in `CartRules`.\n\n```python\n# In examples/shopping_cart_example.py\nfrom pureshell import StatefulEntity, shell_method, ruleset_provider, side_effect_method\n\n@ruleset_provider(CartRules)\nclass ShoppingCart(StatefulEntity):\n    \"\"\"Manages a shopping cart by delegating logic to pure functions.\"\"\"\n    def __init__(self, user_id: str, age: int):\n        self._items: List[CartItem] = []\n        self._profile: UserProfile = UserProfile(user_id=user_id, age=age)\n\n    @shell_method('_items', pure_func='calculate_total')\n    def get_total(self) -\u003e float:\n        \"\"\"Calculates the current total price of all items.\"\"\"\n        raise NotImplementedError()\n\n    @shell_method('_items', mutates=True) # Infers pure_func='add_item'\n    def add_item(self, item: CartItem) -\u003e None:\n        \"\"\"Adds a CartItem to the cart.\"\"\"\n        raise NotImplementedError()\n\n    @shell_method(('_items', '_profile'), pure_func='is_valid')\n    def is_valid_for_checkout(self) -\u003e bool:\n        \"\"\"Checks if the cart is valid for the user.\"\"\"\n        raise NotImplementedError()\n\n    @side_effect_method\n    def display(self):\n        \"\"\"Prints the current state of the cart (a permitted side-effect).\"\"\"\n        # ... implementation for printing state ...\n```\n\n**3. Use your object:**\n\nNow you can use the `ShoppingCart` object. Its methods are simple to call, while the complex logic is handled by the functional core.\n\n```python\n# --- In your main script ---\nadult_cart = ShoppingCart(user_id=\"user-123\", age=30)\nadult_cart.add_item(CartItem(\"Organic Apples\", 4.99))\nadult_cart.add_item(CartItem(\"Craft Beer\", 12.50, requires_age_check=True))\n\nprint(f\"Total: ${adult_cart.get_total():.2f}\")\n# Output: Total: $17.49\n\nprint(f\"Is valid for checkout: {adult_cart.is_valid_for_checkout()}\")\n# Output: Is valid for checkout: True\n```\n\n### 🛑 Why raise NotImplementedError()?\n\nYou'll notice that the decorated methods in the `StatefulEntity` have `raise NotImplementedError()` as their implementation. This is because the `@shell_method` decorator replaces the method with its own logic. This practice ensures that if the decorator is ever misconfigured or accidentally removed, the program will fail immediately and loudly, preventing logic from being silently ignored.\n\nIn addition, this addresses linter warnings that would be raised if `pass` or ellipsis (`...`) were used instead.\n\n## 🎮 Running the Examples\n\nThis repository includes complete, runnable examples to demonstrate the pattern. A helper script is provided to easily run them.\n\nFirst, ensure you have installed the project in editable mode and the development dependencies (see [🚀 Installation](#-installation)).\n\nThen, run the examples module:\n\n```bash\npython -m examples.run\n```\n\nThis will present a menu where you can choose between:\n\n* **Shopping Cart**: The simple e-commerce example detailed above.\n* **Pygame Space Shooter**: A more advanced example showing how `pureshell` can be used to completely separate game logic from the Pygame rendering engine, making the logic highly testable.\n\n## ✅ Running Tests \u0026 Quality Checks\n\nThis project uses `pytest` for testing, `flake8` for linting, `black` for formatting, `mypy` for type checking, and `pre-commit` to automate these checks.\n\n1. **Ensure development dependencies are installed:**\n\n   ```bash\n   pip install -r requirements-dev.txt\n   ```\n\n2. **Run all tests with coverage:**\n\n   ```bash\n   pytest\n   ```\n\n   Coverage reports are generated in HTML format in the `htmlcov/` directory and also printed to the console.\n\n3. **Run linters and formatters manually:**\n\n   ```bash\n   flake8 .\n   black .\n   isort .\n   mypy .\n   ```\n\n4. **Use pre-commit hooks (recommended):**\n\n   Pre-commit hooks will automatically run checks before each commit.\n\n   ```bash\n   pre-commit install\n   ```\n\n   Now, `flake8`, `black`, `isort`, and `mypy` will run on staged files automatically when you commit. If they find issues, the commit will be aborted, allowing you to fix them.\n\n## 📚 Building Documentation\n\nDocumentation is built using Sphinx.\n\n1. **Ensure development dependencies are installed:**\n\n   ```bash\n   pip install -r requirements-dev.txt\n   ```\n\n2. **Build the HTML documentation:**\n\n   ```bash\n   sphinx-build -b html docs docs/_build/html\n   ```\n\n   The generated HTML will be in `docs/_build/html/index.html`.\n\n## CI Pipeline\n\nThis project uses GitHub Actions for Continuous Integration. The workflow is defined in `.github/workflows/ci.yml` and includes:\n\n* Linting with Flake8\n* Formatting checks with Black and isort\n* Type checking with MyPy\n* Running tests with Pytest and generating coverage reports\n* Uploading coverage reports to Codecov (if `CODECOV_TOKEN` is set in repository secrets)\n\n## 🗺️ Roadmap / Future Iterations\n\nThe following features are planned for future versions of `pureshell` to enhance its flexibility and power for more advanced use cases:\n\n* **Dynamic Ruleset Injection**: Allow a `Ruleset` to be injected at instantiation time (`__init__`) rather than just at the class level. This would enable different instances of the same entity to use different logic (e.g., \"Easy\" vs. \"Hard\" mode AI).\n\n* **Advanced Error Handling**: Add a mechanism to the `@shell_method` wrapper to gracefully catch and handle exceptions raised by pure functions, preventing crashes and allowing for more resilient entities.\n\n* **Asynchronous Operations**: Introduce support for `async/await` within the framework, allowing `shell_method` to call and await asynchronous pure functions. This is essential for I/O-bound operations.\n\n* **Memoization for Performance**: Add an optional caching (`memoization`) feature to read-only `@shell_method` calls. This would store and reuse the results of expensive calculations, boosting performance in read-heavy scenarios.\n\n## 🤝 Contributing\n\nContributions are welcome! Please feel free to submit a pull request or open an issue for any bugs, feature requests, or suggestions.\n\n## 📄 License\n\nThis project is licensed under the MIT License.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzinthose%2Fpureshell","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzinthose%2Fpureshell","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzinthose%2Fpureshell/lists"}